Bug 486899 - Keyboard Accessibility on video element (also audio), r=mconnor
☠☠ backed out by f2b6a8093866 ☠ ☠
authorAlexander Surkov <surkov.alexander@gmail.com>
Fri, 22 May 2009 14:54:19 -0400
changeset 25697 d0cc6b5e028a
parent 25696 40faadef447e
child 25698 fc4da433b431
child 25702 f2b6a8093866
push id1542
push userdbolter@mozilla.com
push dateFri, 22 May 2009 19:04:51 +0000
reviewersmconnor
bugs486899
milestone1.9.1pre
Bug 486899 - Keyboard Accessibility on video element (also audio), r=mconnor
accessible/src/xul/nsXULSliderAccessible.cpp
accessible/src/xul/nsXULSliderAccessible.h
accessible/tests/mochitest/Makefile.in
accessible/tests/mochitest/common.js
accessible/tests/mochitest/nsIAccessible_states.js
accessible/tests/mochitest/states.js
accessible/tests/mochitest/test_aria_token_attrs.html
accessible/tests/mochitest/test_elm_media.html
accessible/tests/mochitest/test_nsIAccessible_states.html
accessible/tests/mochitest/test_states_doc.html
accessible/tests/mochitest/test_states_docarticle.html
accessible/tests/mochitest/test_states_editablebody.html
accessible/tests/mochitest/test_states_frames.html
--- a/accessible/src/xul/nsXULSliderAccessible.cpp
+++ b/accessible/src/xul/nsXULSliderAccessible.cpp
@@ -67,16 +67,47 @@ nsXULSliderAccessible::GetRole(PRUint32 
 }
 
 NS_IMETHODIMP
 nsXULSliderAccessible::GetValue(nsAString& aValue)
 {
   return GetSliderAttr(nsAccessibilityAtoms::curpos, aValue);
 }
 
+NS_IMETHODIMP
+nsXULSliderAccessible::GetNumActions(PRUint8 *aCount)
+{
+  NS_ENSURE_ARG_POINTER(aCount);
+
+  *aCount = 1;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULSliderAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
+{
+  aName.Truncate();
+
+  NS_ENSURE_ARG(aIndex == 0);
+
+  aName.AssignLiteral("activate"); 
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULSliderAccessible::DoAction(PRUint8 aIndex)
+{
+  NS_ENSURE_ARG(aIndex == 0);
+
+  nsCOMPtr<nsIContent> sliderContent(GetSliderNode());
+  NS_ENSURE_STATE(sliderContent);
+
+  return DoCommand(sliderContent);
+}
+
 // nsIAccessibleValue
 
 NS_IMETHODIMP
 nsXULSliderAccessible::GetMaximumValue(double *aValue)
 {
   nsresult rv = nsAccessibleWrap::GetMaximumValue(aValue);
 
   // ARIA redefined maximum value.
@@ -129,16 +160,53 @@ nsXULSliderAccessible::SetCurrentValue(d
 
   // ARIA redefined current value.
   if (rv != NS_OK_NO_ARIA_VALUE)
     return rv;
 
   return SetSliderAttr(nsAccessibilityAtoms::curpos, aValue);
 }
 
+// nsPIAccessible
+
+NS_IMETHODIMP
+nsXULSliderAccessible::GetAllowsAnonChildAccessibles(PRBool *aAllowsAnonChildren)
+{
+  NS_ENSURE_ARG_POINTER(aAllowsAnonChildren);
+
+  // Do not allow anonymous xul:slider be accessible.
+  *aAllowsAnonChildren = PR_FALSE;
+  return NS_OK;
+}
+
+// nsAccessible
+
+nsresult
+nsXULSliderAccessible::GetStateInternal(PRUint32 *aState,
+                                        PRUint32 *aExtraState)
+{
+  nsresult rv = nsAccessibleWrap::GetStateInternal(aState, aExtraState);
+  NS_ENSURE_A11Y_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIContent> sliderContent(GetSliderNode());
+  NS_ENSURE_STATE(sliderContent);
+
+  nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mWeakShell));
+  NS_ENSURE_STATE(shell);
+
+  nsIFrame *frame = shell->GetPrimaryFrameFor(sliderContent);
+  if (frame && frame->IsFocusable())
+    *aState |= nsIAccessibleStates::STATE_FOCUSABLE;
+
+  if (gLastFocusedNode == mDOMNode)
+    *aState |= nsIAccessibleStates::STATE_FOCUSED;
+
+  return NS_OK;
+}
+
 // Utils
 
 already_AddRefed<nsIContent>
 nsXULSliderAccessible::GetSliderNode()
 {
   if (!mDOMNode)
     return nsnull;
 
--- a/accessible/src/xul/nsXULSliderAccessible.h
+++ b/accessible/src/xul/nsXULSliderAccessible.h
@@ -49,20 +49,29 @@ public:
   nsXULSliderAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell);
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessible
   NS_IMETHOD GetRole(PRUint32 *aRole);
   NS_IMETHOD GetValue(nsAString& aValue);
+  NS_IMETHOD GetNumActions(PRUint8 *aCount);
+  NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
+  NS_IMETHOD DoAction(PRUint8 aIndex);
 
   // nsIAccessibleValue
   NS_DECL_NSIACCESSIBLEVALUE
 
+  // nsAccessible
+  virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
+
+  // nsPIAccessible
+  NS_IMETHOD GetAllowsAnonChildAccessibles(PRBool *aAllowsAnonChildren);
+
 protected:
   already_AddRefed<nsIContent> GetSliderNode();
 
   nsresult GetSliderAttr(nsIAtom *aName, nsAString& aValue);
   nsresult SetSliderAttr(nsIAtom *aName, const nsAString& aValue);
 
   nsresult GetSliderAttr(nsIAtom *aName, double *aValue);
   nsresult SetSliderAttr(nsIAtom *aName, double aValue);
--- a/accessible/tests/mochitest/Makefile.in
+++ b/accessible/tests/mochitest/Makefile.in
@@ -49,22 +49,22 @@ include $(topsrcdir)/config/rules.mk
 		moz.png \
 		$(topsrcdir)/content/media/video/test/bug461281.ogg \
 		longdesc_src.html \
 		actions.js \
 		attributes.js \
 		common.js \
 		events.js \
 		layout.js \
+		states.js \
 		value.js \
 		nsIAccessible_name.css \
 		nsIAccessible_name.js \
 		nsIAccessible_name.xbl \
  		nsIAccessible_selects.js \
-		nsIAccessible_states.js \
 		nsIAccessibleEditableText.js \
   $(warning test_actions.xul temporarily disabled) \
 		test_actions_aria.html \
 		test_aria_activedescendant.html \
 		test_aria_role_article.html \
 		test_aria_role_equation.html \
  		test_aria_token_attrs.html \
 		$(warning test_bug368835.xul temporarily disabled) \
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -307,26 +307,33 @@ function ensureAccessibleTree(aAccOrElmO
 function testAccessibleTree(aAccOrElmOrID, aAccTree)
 {
   var acc = getAccessible(aAccOrElmOrID);
   if (!acc)
     return;
 
   for (var prop in aAccTree) {
     var msg = "Wrong value of property '" + prop + "'.";
-    if (prop == "role")
+    if (prop == "role") {
       is(roleToString(acc[prop]), roleToString(aAccTree[prop]), msg);
-    else if (prop != "children")
+
+    } else if (prop == "states") {
+      var statesObj = aAccTree[prop];
+      testStates(acc, statesObj.states, statesObj.extraStates,
+                 statesObj.absentStates, statesObj.absentExtraStates);
+
+    } else if (prop != "children") {
       is(acc[prop], aAccTree[prop], msg);
+    }
   }
 
-  if ("children" in aAccTree) {
+  if ("children" in aAccTree && aAccTree["children"] instanceof Array) {
     var children = acc.children;
     is(children.length, aAccTree.children.length,
-       "Different amount of expected children.");
+       "Different amount of expected children of " + prettyName(acc) + ".");
 
     if (aAccTree.children.length == children.length) { 
       for (var i = 0; i < children.length; i++) {
         var child = children.queryElementAt(i, nsIAccessible);
         testAccessibleTree(child, aAccTree.children[i]);
       }
     }
   }
rename from accessible/tests/mochitest/nsIAccessible_states.js
rename to accessible/tests/mochitest/states.js
--- a/accessible/tests/mochitest/nsIAccessible_states.js
+++ b/accessible/tests/mochitest/states.js
@@ -1,42 +1,44 @@
 function testStates(aAccOrElmOrID, aState, aExtraState, aAbsentState)
 {
   var [state, extraState] = getStates(aAccOrElmOrID);
 
+  var id = prettyName(aAccOrElmOrID);
+
   is(state & aState, aState,
-     "wrong state bits for " + aAccOrElmOrID + "!");
+     "wrong state bits for " + id + "!");
 
   if (aExtraState)
     is(extraState & aExtraState, aExtraState,
-       "wrong extra state bits for " + aAccOrElmOrID + "!");
+       "wrong extra state bits for " + id + "!");
 
   if (aAbsentState)
     is(state & aAbsentState, 0,
-       "state bits should not be present in ID " + aAccOrElmOrID + "!");
+       "state bits should not be present in ID " + id + "!");
 
   if (state & STATE_READONLY)
     is(extraState & EXT_STATE_EDITABLE, 0,
-       "Read-only " + aAccOrElmOrID + " cannot be editable!");
+       "Read-only " + id + " cannot be editable!");
 
   if (extraState & EXT_STATE_EDITABLE)
     is(state & STATE_READONLY, 0,
-       "Editable " + aAccOrElmOrID + " cannot be readonly!");
+       "Editable " + id + " cannot be readonly!");
 
   if (state & STATE_COLLAPSED || state & STATE_EXPANDED)
     is(extraState & EXT_STATE_EXPANDABLE, EXT_STATE_EXPANDABLE,
-       "Collapsed or expanded " + aAccOrElmOrID + " should be expandable!");
+       "Collapsed or expanded " + id + " should be expandable!");
 
   if (state & STATE_COLLAPSED)
     is(state & STATE_EXPANDED, 0,
-       "Collapsed " + aAccOrElmOrID + " cannot be expanded!");
+       "Collapsed " + id + " cannot be expanded!");
 
   if (state & STATE_EXPANDED)
     is(state & STATE_COLLAPSED, 0,
-       "Expanded " + aAccOrElmOrID + " cannot be collapsed!");
+       "Expanded " + id + " cannot be collapsed!");
 }
 
 function getStringStates(aAccOrElmOrID)
 {
   var [state, extraState] = getStates(aAccOrElmOrID);
   var list = gAccRetrieval.getStringStates(state, extraState);
 
   var str = "";
--- a/accessible/tests/mochitest/test_aria_token_attrs.html
+++ b/accessible/tests/mochitest/test_aria_token_attrs.html
@@ -10,17 +10,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <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_states.js"></script>
+      src="chrome://mochikit/content/a11y/accessible/states.js"></script>
 
   <script type="application/javascript">
     function doTest()
     {
        // test (treeitem) selectable and selected states
       testStates("treeitem_selected_true", (STATE_SELECTABLE | STATE_SELECTED));
       testStates("treeitem_selected_false", STATE_SELECTABLE, 0, STATE_SELECTED);
       testStates("treeitem_selected_empty", 0, 0, (STATE_SELECTABLE | STATE_SELECTED));
--- a/accessible/tests/mochitest/test_elm_media.html
+++ b/accessible/tests/mochitest/test_elm_media.html
@@ -13,94 +13,121 @@ https://bugzilla.mozilla.org/show_bug.cg
           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/events.js"></script>
   <script type="application/javascript"
           src="chrome://mochikit/content/a11y/accessible/actions.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/a11y/accessible/states.js"></script>
 
   <script type="application/javascript">
+
+    // gA11yEventDumpID = "eventDump";
+
     function focusChecker(aAcc)
     {
       this.type = EVENT_FOCUS;
       this.target = aAcc;
       this.getID = function focusChecker_getID()
       {
         return "focus handling";
       }
+      this.check = function focusChecker_check(aEvent)
+      {
+        testStates(this.target, STATE_FOCUSED);
+      }
     }
 
     function nameChecker(aAcc, aName)
     {
       this.type = EVENT_NAME_CHANGE;
       this.target = aAcc;
       this.getID = function nameChecker_getID()
       {
         return "name change handling";
       },
-      this.check = function focusChecker_check(aEvent)
+      this.check = function nameChecker_check(aEvent)
       {
         is(aEvent.accessible.name, aName,
            "Wrong name of " + prettyName(aEvent.accessible) + " on focus");
       }
     }
 
     function doTest()
     {
       //////////////////////////////////////////////////////////////////////////
       // test the accessible tree
 
       var accTree = {
         role: ROLE_GROUPING,
         children: [
           { // start/stop button
             role: ROLE_PUSHBUTTON,
-            name: "Play"
+            name: "Play",
+            states: {
+              states: STATE_FOCUSABLE
+            }
           },
           { // buffer bar
             role: ROLE_PROGRESSBAR
           },
           { // progress bar
             role: ROLE_PROGRESSBAR
           },
           { // slider of progress bar
             role: ROLE_SLIDER,
-            name: "0:00 of 0:01 elapsed"
+            name: "0:00 of 0:01 elapsed",
+            states: {
+              states: STATE_FOCUSABLE
+            }
           },
           { // duration label, role="presentation"
             role: ROLE_LABEL
           },
           { // volume button
             role: ROLE_PUSHBUTTON,
-            name: "Mute"
+            name: "Mute",
+            states: {
+              states: STATE_FOCUSABLE
+            }
           }
         ]
       };
       testAccessibleTree("audio", accTree);
 
       //////////////////////////////////////////////////////////////////////////
       // test actions of audio controls
 
       var audioElm = getAccessible("audio");
       var playBtn = audioElm.firstChild;
+      var scrubber = playBtn.nextSibling.nextSibling.nextSibling;
       var muteBtn = audioElm.lastChild;
 
       var actions = [
         {
           ID: muteBtn,
           actionName: "press",
           events: CLICK_EVENTS,
           eventSeq: [
             new focusChecker(muteBtn),
             new nameChecker(muteBtn, "Unmute"),
           ]
         },
         {
+          ID: scrubber,
+          actionName: "activate",
+          events: null,
+          eventSeq: [
+            new focusChecker(scrubber)
+          ]
+        },
+        {
           ID: playBtn,
           actionName: "press",
           events: CLICK_EVENTS,
           eventSeq: [
             new focusChecker(playBtn),
             new nameChecker(playBtn, "Pause"),
           ]
         }
@@ -120,10 +147,12 @@ https://bugzilla.mozilla.org/show_bug.cg
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=483573">Mozilla Bug 483573</a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <audio id="audio" src="chrome://mochikit/content/a11y/accessible/bug461281.ogg"
          controls="true">
+
+  <div id="eventDump"></div>
 </body>
 </html>
--- a/accessible/tests/mochitest/test_nsIAccessible_states.html
+++ b/accessible/tests/mochitest/test_nsIAccessible_states.html
@@ -9,17 +9,17 @@
   <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_states.js"></script>
+          src="chrome://mochikit/content/a11y/accessible/states.js"></script>
 
   <script type="application/javascript">
     function doTest()
     {
       testStates("combobox", STATE_COLLAPSED);
       testStates("combobox_expanded", STATE_EXPANDED);
 
       SimpleTest.finish();
--- a/accessible/tests/mochitest/test_states_doc.html
+++ b/accessible/tests/mochitest/test_states_doc.html
@@ -11,17 +11,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <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_states.js"></script>
+          src="chrome://mochikit/content/a11y/accessible/states.js"></script>
 
   <script type="application/javascript">
     function doTest()
     {
       testStates(document, STATE_READONLY);
       testStates("document", STATE_READONLY);
       testStates("editable_document", 0, EXT_STATE_EDITABLE);
 
--- a/accessible/tests/mochitest/test_states_docarticle.html
+++ b/accessible/tests/mochitest/test_states_docarticle.html
@@ -8,17 +8,17 @@
   <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_states.js"></script>
+          src="chrome://mochikit/content/a11y/accessible/states.js"></script>
 
   <script type="application/javascript">
     function doTest()
     {      
       var docAcc = getAccessible(document, [nsIAccessibleDocument]);
       if (docAcc) {
         testStates(docAcc, STATE_READONLY);
         testStates("article", STATE_READONLY);
--- a/accessible/tests/mochitest/test_states_editablebody.html
+++ b/accessible/tests/mochitest/test_states_editablebody.html
@@ -10,17 +10,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <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_states.js"></script>
+          src="chrome://mochikit/content/a11y/accessible/states.js"></script>
 
   <script type="application/javascript">
     function doTest()
     {
       testStates(document, 0, EXT_STATE_EDITABLE);
       testStates("p", 0, EXT_STATE_EDITABLE);
 
       SimpleTest.finish();
--- a/accessible/tests/mochitest/test_states_frames.html
+++ b/accessible/tests/mochitest/test_states_frames.html
@@ -9,17 +9,17 @@
   <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_states.js"></script>
+          src="chrome://mochikit/content/a11y/accessible/states.js"></script>
 
   <script type="application/javascript">
     function doTest()
     {
       frameDoc = document.getElementById("frame_doc").contentDocument;
       frameDocArticle = document.getElementById("frame_doc_article").contentDocument;
       frameDocCheckbox = document.getElementById("frame_doc_checkbox").contentDocument;
       frameDocTextbox = document.getElementById("frame_doc_textbox").contentDocument;