Bug 1251743 - ARIA owns reallocation may insert a child at wrong index, r=yzen
authorAlexander Surkov <surkov.alexander@gmail.com>
Tue, 01 Mar 2016 14:35:01 -0500
changeset 324588 134e5a1235557d8ffa0a9b04dd00fef1ff10ae3e
parent 324587 e13aaaaf196229acb1bcd77f0ab9e5ad3ada7d9a
child 324589 7e80b936414e259da594168b3791d5d4ccd5b9d9
push id1128
push userjlund@mozilla.com
push dateWed, 01 Jun 2016 01:31:59 +0000
treeherdermozilla-release@fe0d30de989d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersyzen
bugs1251743
milestone47.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1251743 - ARIA owns reallocation may insert a child at wrong index, r=yzen
accessible/generic/DocAccessible.cpp
accessible/tests/mochitest/treeupdate/test_ariaowns.html
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -2024,16 +2024,17 @@ DocAccessible::DoARIAOwnsRelocation(Acce
 
     if (child->Parent() == aOwner) {
       if (child->IsRelocated()) {
         children->RemoveElement(child);
       }
       MoveChild(child, insertIdx);
       children->InsertElementAt(arrayIdx, child);
       arrayIdx++;
+      insertIdx = child->IndexInParent() + 1;
 
     } else if (SeizeChild(aOwner, child, insertIdx)) {
       children->InsertElementAt(arrayIdx, child);
       insertIdx++; arrayIdx++;
     }
   }
 
   // Put back children that are not seized anymore.
@@ -2050,31 +2051,42 @@ DocAccessible::SeizeChild(Accessible* aN
   Accessible* oldParent = aChild->Parent();
   if (!oldParent) {
     NS_ERROR("No parent? The tree is broken!");
     return false;
   }
 
   int32_t oldIdxInParent = aChild->IndexInParent();
 
+#ifdef A11Y_LOG
+  logging::TreeInfo("aria owns seize child", 0,
+                    "old parent", oldParent, "new parent", aNewParent,
+                    "child", aChild, nullptr);
+#endif
+
   RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(oldParent);
   RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(aChild, false);
   reorderEvent->AddSubMutationEvent(hideEvent);
 
   {
     AutoTreeMutation mut(oldParent);
     oldParent->RemoveChild(aChild);
   }
 
   bool isReinserted = false;
   {
     AutoTreeMutation mut(aNewParent);
     isReinserted = aNewParent->InsertChildAt(aIdxInParent, aChild);
   }
 
+#ifdef A11Y_LOG
+    logging::TreeInfo("aria owns seize child: new parent tree after",
+                      logging::eVerbose, aNewParent);
+#endif
+
   if (!isReinserted) {
     AutoTreeMutation mut(oldParent);
     oldParent->InsertChildAt(oldIdxInParent, aChild);
     return false;
   }
 
   // The child may be stolen from other ARIA owns element.
   if (aChild->IsRelocated()) {
@@ -2103,20 +2115,30 @@ DocAccessible::MoveChild(Accessible* aCh
 {
   NS_PRECONDITION(aChild->Parent(), "No parent?");
 
   Accessible* parent = aChild->Parent();
   RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(parent);
   RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(aChild, false);
   reorderEvent->AddSubMutationEvent(hideEvent);
 
+#ifdef A11Y_LOG
+  logging::TreeInfo("aria owns move child", 0,
+                    "parent", parent, "child", aChild, nullptr);
+#endif
+
   AutoTreeMutation mut(parent);
   parent->MoveChild(aIdxInParent, aChild);
   aChild->SetRelocated(true);
 
+#ifdef A11Y_LOG
+  logging::TreeInfo("aria owns move child: parent tree after",
+                    logging::eVerbose, parent);
+#endif
+
   FireDelayedEvent(hideEvent);
 
   RefPtr<AccMutationEvent> showEvent = new AccShowEvent(aChild);
   reorderEvent->AddSubMutationEvent(showEvent);
   FireDelayedEvent(showEvent);
 
   MaybeNotifyOfValueChange(parent);
   FireDelayedEvent(reorderEvent);
--- a/accessible/tests/mochitest/treeupdate/test_ariaowns.html
+++ b/accessible/tests/mochitest/treeupdate/test_ariaowns.html
@@ -436,22 +436,54 @@
       }
 
       this.getID = function showHiddenElement_getID()
       {
         return "Show hidden ARIA owns referred element";
       }
     }
 
+    function rearrangeARIAOwns(aContainer, aAttr, aIdList, aRoleList)
+    {
+      this.eventSeq = [];
+      for (var id of aIdList) {
+        this.eventSeq.push(new invokerChecker(EVENT_HIDE, getNode(id)));
+        this.eventSeq.push(new invokerChecker(EVENT_SHOW, getNode(id)));
+      }
+      this.eventSeq.push(new invokerChecker(EVENT_REORDER, getNode(aContainer)));
+
+      this.invoke = function rearrangeARIAOwns_invoke()
+      {
+        getNode(aContainer).setAttribute("aria-owns", aAttr);
+      }
+
+      this.finalCheck = function rearrangeARIAOwns_finalCheck()
+      {
+        var tree = { SECTION: [ ] };
+        for (var role of aRoleList) {
+          var ch = {};
+          ch[role] = [];
+          tree["SECTION"].push(ch);
+        }
+        testAccessibleTree(aContainer, tree);
+      }
+
+      this.getID = function rearrangeARIAOwns_getID()
+      {
+        return `Rearrange @aria-owns attribute to '${aAttr}'`;
+      }
+    }
+
+
     ////////////////////////////////////////////////////////////////////////////
     // Test
     ////////////////////////////////////////////////////////////////////////////
 
     //gA11yEventDumpToConsole = true;
-    //enableLogging("tree"); // debug stuff
+    //enableLogging("tree,verbose"); // debug stuff
 
     var gQueue = null;
 
     function doTest()
     {
       gQueue = new eventQueue();
 
       // test1
@@ -469,16 +501,26 @@
 
       // test3
       gQueue.push(new stealFromOtherARIAOwns());
       gQueue.push(new appendElToRecacheChildren());
 
       // test4
       gQueue.push(new showHiddenElement());
 
+      // test5
+      gQueue.push(new rearrangeARIAOwns(
+        "t5_container", "t5_checkbox t5_radio t5_button",
+        [ "t5_checkbox", "t5_radio", "t5_button" ],
+        [ "CHECKBUTTON", "RADIOBUTTON", "PUSHBUTTON" ]));
+      gQueue.push(new rearrangeARIAOwns(
+        "t5_container", "t5_radio t5_button t5_checkbox",
+        [ "t5_radio", "t5_button" ],
+        [ "RADIOBUTTON", "PUSHBUTTON", "CHECKBUTTON" ]));
+
       gQueue.invoke(); // SimpleTest.finish() will be called in the end
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
 
   </script>
 </head>
@@ -510,11 +552,17 @@
   <div id="t3_child" role="checkbox"></div>
   <div id="t3_container2"></div>
 
   <div id="t4_container1" aria-owns="t4_child1 t4_child2"></div>
   <div id="t4_container2">
     <div id="t4_child1" style="display:none" role="checkbox"></div>
     <div id="t4_child2" role="radio"></div>
   </div>
+
+  <div id="t5_container">
+    <div role="button" id="t5_button"></div>
+    <div role="checkbox" id="t5_checkbox"></div>
+    <div role="radio" id="t5_radio"></div>
+  </div>
 </body>
 
 </html>