Bug 1263188 - fix event tree cutting, part1, r=yzen
authorAlexander Surkov <surkov.alexander@gmail.com>
Tue, 12 Apr 2016 15:48:29 -0400
changeset 330774 547dbf605e71dcffcca5f735dbefdf55d0e0423a
parent 330773 fbd4d72adc4c45cb4d2daf4734c69120023e5da3
child 330775 6fc0294f96c5b74a19da133a260df51677d64c3f
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersyzen
bugs1263188
milestone48.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 1263188 - fix event tree cutting, part1, r=yzen
accessible/base/EventTree.cpp
accessible/base/EventTree.h
accessible/tests/mochitest/events/test_coalescence.html
--- a/accessible/base/EventTree.cpp
+++ b/accessible/base/EventTree.cpp
@@ -223,16 +223,18 @@ EventTree::Process()
         }
       }
     }
 
     // Fire reorder event at last.
     if (mFireReorder) {
       nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_REORDER, mContainer);
     }
+
+    mDependentEvents.Clear();
   }
 }
 
 EventTree*
 EventTree::FindOrInsert(Accessible* aContainer)
 {
   if (!mFirst) {
     return mFirst = new EventTree(aContainer);
@@ -379,16 +381,33 @@ EventTree::FindOrInsert(Accessible* aCon
 
     prevNode = node;
   } while ((node = node->mNext));
 
   MOZ_ASSERT(prevNode, "Nowhere to insert");
   return prevNode->mNext = new EventTree(aContainer);
 }
 
+void
+EventTree::Clear()
+{
+  mFirst = nullptr;
+  mNext = nullptr;
+  mContainer = nullptr;
+
+  uint32_t eventsCount = mDependentEvents.Length();
+  for (uint32_t jdx = 0; jdx < eventsCount; jdx++) {
+    AccHideEvent* ev = downcast_accEvent(mDependentEvents[jdx]);
+    if (ev && ev->NeedsShutdown()) {
+      ev->GetDocAccessible()->ShutdownChildrenInSubtree(ev->mAccessible);
+    }
+  }
+  mDependentEvents.Clear();
+}
+
 const EventTree*
 EventTree::Find(const Accessible* aContainer) const
 {
   const EventTree* et = this;
   while (et) {
     if (et->mContainer == aContainer) {
       return et;
     }
@@ -453,23 +472,23 @@ EventTree::Log(uint32_t aLevel) const
 }
 #endif
 
 void
 EventTree::Mutated(AccMutationEvent* aEv)
 {
   // If shown or hidden node is a root of previously mutated subtree, then
   // discard those subtree mutations as we are no longer interested in them.
-  EventTree* node = mFirst;
-  while (node) {
-    if (node->mContainer == aEv->mAccessible) {
-      node->Clear();
+  nsAutoPtr<EventTree>* node = &mFirst;
+  while (*node) {
+    if ((*node)->mContainer == aEv->mAccessible) {
+      *node = Move((*node)->mNext);
       break;
     }
-    node = node->mNext;
+    node = &(*node)->mNext;
   }
 
   AccMutationEvent* prevEvent = mDependentEvents.SafeLastElement(nullptr);
   mDependentEvents.AppendElement(aEv);
 
   // Coalesce text change events from this hide/show event and the previous one.
   if (prevEvent && aEv->mEventType == prevEvent->mEventType) {
     if (aEv->IsHide()) {
--- a/accessible/base/EventTree.h
+++ b/accessible/base/EventTree.h
@@ -51,17 +51,17 @@ private:
 
 
 /**
  * A mutation events coalescence structure.
  */
 class EventTree final {
 public:
   EventTree() :
-    mFirst(nullptr), mNext(nullptr), mContainer(nullptr), mFireReorder(true) { }
+    mFirst(nullptr), mNext(nullptr), mContainer(nullptr), mFireReorder(false) { }
   explicit EventTree(Accessible* aContainer) :
     mFirst(nullptr), mNext(nullptr), mContainer(aContainer), mFireReorder(true) { }
   ~EventTree() { Clear(); }
 
   void Shown(Accessible* aChild)
   {
     RefPtr<AccShowEvent> ev = new AccShowEvent(aChild);
     Mutated(ev);
@@ -89,17 +89,17 @@ private:
   void Process();
 
   /**
    * Return an event subtree for the given accessible.
    */
   EventTree* FindOrInsert(Accessible* aContainer);
 
   void Mutated(AccMutationEvent* aEv);
-  void Clear() { mFirst = nullptr; mNext = nullptr; mContainer = nullptr; }
+  void Clear();
 
   nsAutoPtr<EventTree> mFirst;
   nsAutoPtr<EventTree> mNext;
 
   Accessible* mContainer;
   nsTArray<RefPtr<AccMutationEvent>> mDependentEvents;
   bool mFireReorder;
 
--- a/accessible/tests/mochitest/events/test_coalescence.html
+++ b/accessible/tests/mochitest/events/test_coalescence.html
@@ -9,16 +9,18 @@
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
+          src="../states.js"></script>
+  <script type="application/javascript"
           src="../events.js"></script>
 
   <script type="application/javascript">
 
     ////////////////////////////////////////////////////////////////////////////
     // Invoker base classes
 
     const kRemoveElm = 1;
@@ -344,21 +346,91 @@
         this.parent.hidden = true;
       }
 
       this.getID = function removeGrandChildrenNHideParent_getID() {
         return "remove grand children of different parents and then hide their grand parent";
       }
     }
 
+    /**
+     * Remove a child, and then its parent.
+     */
+    function test3()
+    {
+      this.o = getAccessible("t3_o");
+      this.ofc = getAccessible("t3_o").firstChild;
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_HIDE, this.o),
+        new invokerChecker(EVENT_REORDER, "t3_lb"),
+        new unexpectedInvokerChecker(EVENT_HIDE, this.ofc),
+        new unexpectedInvokerChecker(EVENT_REORDER, this.o)
+      ];
+
+      this.invoke = function test3_invoke()
+      {
+        getNode("t3_o").textContent = "";
+        getNode("t3_lb").removeChild(getNode("t3_o"));
+      }
+
+      this.finalCheck = function test3_finalCheck()
+      {
+        testIsDefunct(this.o);
+        testIsDefunct(this.ofc);
+      }
+
+      this.getID = function test3_getID() {
+        return "remove a child, and then its parent";
+      }
+    }
+
+    /**
+     * Remove children, and then a parent of 2nd child.
+     */
+    function test4()
+    {
+      this.o1 = getAccessible("t4_o1");
+      this.o1fc = this.o1.firstChild;
+      this.o2 = getAccessible("t4_o2");
+      this.o2fc = this.o2.firstChild;
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_HIDE, this.o1fc),
+        new invokerChecker(EVENT_HIDE, this.o2),
+        new invokerChecker(EVENT_REORDER, "t4_lb"),
+        new unexpectedInvokerChecker(EVENT_HIDE, this.o2fc),
+        new unexpectedInvokerChecker(EVENT_REORDER, this.o1),
+        new unexpectedInvokerChecker(EVENT_REORDER, this.o2)
+      ];
+
+      this.invoke = function test4_invoke()
+      {
+        getNode("t4_o1").textContent = "";
+        getNode("t4_o2").textContent = "";
+        getNode("t4_lb").removeChild(getNode("t4_o2"));
+      }
+
+      this.finalCheck = function test4_finalCheck()
+      {
+        testIsDefunct(this.o1fc);
+        testIsDefunct(this.o2);
+        testIsDefunct(this.o2fc);
+      }
+
+      this.getID = function test4_getID() {
+        return "remove children, and then a parent of 2nd child";
+      }
+    }
+
     ////////////////////////////////////////////////////////////////////////////
     // Do tests.
 
     //gA11yEventDumpToConsole = true; // debug stuff
-    //enableLogging("events,tree,eventTree,verbose");
+    //enableLogging("tree,eventTree,verbose");
 
     var gQueue = null;
     function doTests()
     {
       gQueue = new eventQueue();
 
       gQueue.push(new removeChildNParent("option1", "select1"));
       gQueue.push(new removeParentNChild("option2", "select2"));
@@ -372,16 +444,18 @@
       gQueue.push(new addParentNChild("testContainer", false));
       gQueue.push(new addParentNChild("testContainer", true));
       gQueue.push(new showParentNChild("select9", "option9", false));
       gQueue.push(new showParentNChild("select10", "option10", true));
       gQueue.push(new showParentNAddChild("select11", false));
       gQueue.push(new showParentNAddChild("select12", true));
 
       gQueue.push(new removeGrandChildrenNHideParent("t1_child1", "t1_child2", "t1_parent"));
+      gQueue.push(new test3());
+      gQueue.push(new test4());
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
@@ -442,10 +516,23 @@
   </div>
 
   <div id="testContainer2">
     <div id="t1_parent">
       <div id="t1_mid1"><div id="t1_child1"></div></div>
       <div id="t1_mid2"><div id="t1_child2"></div></div>
     </div>
   </div>
+
+  <div id="t3">
+    <div role="listbox" id="t3_lb">
+      <div role="option" id="t3_o">opt</div>
+    </div>
+  </div>
+
+  <div id="t4">
+    <div role="listbox" id="t4_lb">
+      <div role="option" id="t4_o1">opt1</div>
+      <div role="option" id="t4_o2">opt2</div>
+    </div>
+  </div>
 </body>
 </html>