Bug 1260494 - rebuild child indexes by AutoTreeMutation guard, r=yzen
authorAlexander Surkov <surkov.alexander@gmail.com>
Fri, 01 Apr 2016 10:53:52 -0400
changeset 291249 9839c4bda97608c9fc2dcecb6e767386a4c9215d
parent 291248 f53dbc1c638a131bff1b5b49ce08d71a8f0a56c9
child 291250 8d8a214a21b3276814b126656982141802981ff5
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersyzen
bugs1260494
milestone48.0a1
Bug 1260494 - rebuild child indexes by AutoTreeMutation guard, r=yzen
accessible/generic/Accessible.cpp
accessible/generic/Accessible.h
accessible/generic/DocAccessible.cpp
accessible/html/HTMLImageMapAccessible.cpp
accessible/html/HTMLListAccessible.cpp
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -1963,42 +1963,37 @@ Accessible::NativeName(nsString& aName)
 void
 Accessible::BindToParent(Accessible* aParent, uint32_t aIndexInParent)
 {
   NS_PRECONDITION(aParent, "This method isn't used to set null parent!");
 
   if (mParent) {
     if (mParent != aParent) {
 #ifdef A11Y_LOG
-      if (logging::IsEnabled(logging::eTree)) {
-        logging::MsgBegin("TREE", "BindToParent: stealing accessible");
-        logging::AccessibleInfo("old parent", mParent);
-        logging::AccessibleInfo("new parent", aParent);
-        logging::AccessibleInfo("child", this);
-        logging::MsgEnd();
-      }
+      logging::TreeInfo("BindToParent: stealing accessible", 0,
+                        "old parent", mParent.get(),
+                        "new parent", aParent,
+                        "child", this, nullptr);
 #endif
       // XXX: legalize adoption. As long as we don't invalidate the children,
       // the accessibles start to steal them.
 
-      mParent->InvalidateChildrenGroupInfo();
+      AutoTreeMutation mt(mParent);
+      mt.BeforeRemoval(this);
       mParent->RemoveChild(this);
+      mt.Done();
     } else {
       NS_ERROR("Binding to the same parent!");
       return;
     }
   }
 
   mParent = aParent;
   mIndexInParent = aIndexInParent;
 
-#ifdef DEBUG
-  AssertInMutatingSubtree();
-#endif
-
   // Note: this is currently only used for richlistitems and their children.
   if (mParent->HasNameDependentParent() || mParent->IsXULListItem())
     mContextFlags |= eHasNameDependentParent;
   else
     mContextFlags &= ~eHasNameDependentParent;
 
   if (mParent->IsARIAHidden() || aria::HasDefinedARIAHidden(mContent))
     SetARIAHidden(true);
@@ -2007,19 +2002,16 @@ Accessible::BindToParent(Accessible* aPa
     static_cast<uint32_t>((mParent->IsAlert() ||
                            mParent->IsInsideAlert())) & eInsideAlert;
 }
 
 // Accessible protected
 void
 Accessible::UnbindFromParent()
 {
-#ifdef DEBUG
-  AssertInMutatingSubtree();
-#endif
   mParent = nullptr;
   mIndexInParent = -1;
   mInt.mIndexOfEmbeddedChild = -1;
   if (IsProxy())
     MOZ_CRASH("this should never be called on proxy wrappers");
 
   delete mBits.groupInfo;
   mBits.groupInfo = nullptr;
@@ -2085,23 +2077,17 @@ Accessible::InsertChildAt(uint32_t aInde
   if (aIndex == mChildren.Length()) {
     if (!mChildren.AppendElement(aChild))
       return false;
 
   } else {
     if (!mChildren.InsertElementAt(aIndex, aChild))
       return false;
 
-    for (uint32_t idx = aIndex + 1; idx < mChildren.Length(); idx++) {
-      NS_ASSERTION(static_cast<uint32_t>(mChildren[idx]->mIndexInParent) == idx - 1,
-                   "Accessible child index doesn't match");
-      mChildren[idx]->mIndexInParent = idx;
-    }
-
-    mEmbeddedObjCollector = nullptr;
+    MOZ_ASSERT(mStateFlags & eKidsMutating, "Illicit children change");
   }
 
   if (!nsAccUtils::IsEmbeddedObject(aChild))
     SetChildrenFlag(eMixedChildren);
 
   aChild->BindToParent(this, aIndex);
   return true;
 }
@@ -2110,51 +2096,44 @@ bool
 Accessible::RemoveChild(Accessible* aChild)
 {
   if (!aChild)
     return false;
 
   if (aChild->mParent != this || aChild->mIndexInParent == -1)
     return false;
 
-  uint32_t index = static_cast<uint32_t>(aChild->mIndexInParent);
-  if (index >= mChildren.Length() || mChildren[index] != aChild) {
-    NS_ERROR("Child is bound to parent but parent hasn't this child at its index!");
-    aChild->UnbindFromParent();
-    return false;
-  }
-
-  for (uint32_t idx = index + 1; idx < mChildren.Length(); idx++) {
-    NS_ASSERTION(static_cast<uint32_t>(mChildren[idx]->mIndexInParent) == idx,
-                 "Accessible child index doesn't match");
-    mChildren[idx]->mIndexInParent = idx - 1;
+  MOZ_ASSERT((mStateFlags & eKidsMutating) || aChild->IsDefunct() || aChild->IsDoc(),
+             "Illicit children change");
+
+  int32_t index = static_cast<uint32_t>(aChild->mIndexInParent);
+
+  // If we adopt a child during a tree construction, then indexes might be not
+  // rebuilt yet.
+  if (mChildren.SafeElementAt(index) != aChild) {
+    index = mChildren.IndexOf(aChild);
+    MOZ_ASSERT(index != -1,
+               "Child is bound to parent but parent hasn't this child at its index.");
   }
 
   aChild->UnbindFromParent();
   mChildren.RemoveElementAt(index);
-  mEmbeddedObjCollector = nullptr;
-
   return true;
 }
 
 void
 Accessible::MoveChild(uint32_t aNewIndex, Accessible* aChild)
 {
   MOZ_ASSERT(aChild, "No child was given");
   MOZ_ASSERT(aChild->mParent == this, "A child from different subtree was given");
   MOZ_ASSERT(aChild->mIndexInParent != -1, "Unbound child was given");
   MOZ_ASSERT(static_cast<uint32_t>(aChild->mIndexInParent) != aNewIndex,
              "No move, same index");
   MOZ_ASSERT(aNewIndex <= mChildren.Length(), "Wrong new index was given");
 
-#ifdef DEBUG
-  // AutoTreeMutation should update group info.
-  AssertInMutatingSubtree();
-#endif
-
   mEmbeddedObjCollector = nullptr;
   mChildren.RemoveElementAt(aChild->mIndexInParent);
 
   uint32_t startIdx = aNewIndex, endIdx = aChild->mIndexInParent;
 
   // If the child is moved after its current position.
   if (static_cast<uint32_t>(aChild->mIndexInParent) < aNewIndex) {
     startIdx = aChild->mIndexInParent;
@@ -2171,16 +2150,17 @@ Accessible::MoveChild(uint32_t aNewIndex
   }
   else {
     // The child is moved prior its current position.
     mChildren.InsertElementAt(aNewIndex, aChild);
   }
 
   for (uint32_t idx = startIdx; idx <= endIdx; idx++) {
     mChildren[idx]->mIndexInParent = idx;
+    mChildren[idx]->mStateFlags |= eGroupInfoDirty;
     mChildren[idx]->mInt.mIndexOfEmbeddedChild = -1;
   }
 }
 
 Accessible*
 Accessible::GetChildAt(uint32_t aIndex) const
 {
   Accessible* child = mChildren.SafeElementAt(aIndex, nullptr);
@@ -2549,20 +2529,23 @@ Accessible::LastRelease()
   delete this;
 }
 
 void
 Accessible::CacheChildren()
 {
   NS_ENSURE_TRUE_VOID(Document());
 
+  AutoTreeMutation mt(this);
   TreeWalker walker(this);
-
   Accessible* child = nullptr;
-  while ((child = walker.Next()) && AppendChild(child));
+  while ((child = walker.Next()) && AppendChild(child)) {
+    mt.AfterInsertion(child);
+  }
+  mt.Done();
 }
 
 void
 Accessible::TestChildCache(Accessible* aCachedChild) const
 {
 #ifdef DEBUG
   int32_t childCount = mChildren.Length();
   if (childCount == 0) {
@@ -2670,37 +2653,27 @@ AccGroupInfo*
 Accessible::GetGroupInfo()
 {
   if (IsProxy())
     MOZ_CRASH("This should never be called on proxy wrappers");
 
   if (mBits.groupInfo){
     if (HasDirtyGroupInfo()) {
       mBits.groupInfo->Update();
-      SetDirtyGroupInfo(false);
+      mStateFlags &= ~eGroupInfoDirty;
     }
 
     return mBits.groupInfo;
   }
 
   mBits.groupInfo = AccGroupInfo::CreateGroupInfo(this);
   return mBits.groupInfo;
 }
 
 void
-Accessible::InvalidateChildrenGroupInfo()
-{
-  uint32_t length = mChildren.Length();
-  for (uint32_t i = 0; i < length; i++) {
-    Accessible* child = mChildren[i];
-    child->SetDirtyGroupInfo(true);
-  }
-}
-
-void
 Accessible::GetPositionAndSizeInternal(int32_t *aPosInSet, int32_t *aSetSize)
 {
   AccGroupInfo* groupInfo = GetGroupInfo();
   if (groupInfo) {
     *aPosInSet = groupInfo->PosInSet();
     *aSetSize = groupInfo->SetSize();
   }
 }
@@ -2783,32 +2756,16 @@ Accessible::StaticAsserts() const
   static_assert(eLastAccType <= (1 << kTypeBits) - 1,
                 "Accessible::mType was oversized by eLastAccType!");
   static_assert(eLastContextFlag <= (1 << kContextFlagsBits) - 1,
                 "Accessible::mContextFlags was oversized by eLastContextFlag!");
   static_assert(eLastAccGenericType <= (1 << kGenericTypesBits) - 1,
                 "Accessible::mGenericType was oversized by eLastAccGenericType!");
 }
 
-void
-Accessible::AssertInMutatingSubtree() const
-{
-  if (IsDoc() || IsApplication())
-    return;
-
-  const Accessible *acc = this;
-  while (!acc->IsDoc() && !(acc->mStateFlags & eSubtreeMutating)) {
-    acc = acc->Parent();
-    if (!acc)
-      return;
-  }
-
-  MOZ_ASSERT(acc->mStateFlags & eSubtreeMutating);
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // KeyBinding class
 
 // static
 uint32_t
 KeyBinding::AccelModifier()
 {
   switch (WidgetInputEvent::AccelModifier()) {
@@ -2894,8 +2851,38 @@ KeyBinding::ToAtkFormat(nsAString& aValu
     aValue.AppendLiteral("<Shift>");
 
   if (mModifierMask & kMeta)
       aValue.AppendLiteral("<Meta>");
 
   aValue.Append(mKey);
 }
 
+
+////////////////////////////////////////////////////////////////////////////////
+// AutoTreeMutation class
+
+void
+AutoTreeMutation::Done()
+{
+  MOZ_ASSERT(mParent->mStateFlags & Accessible::eKidsMutating,
+             "The parent is not in mutating state.");
+  mParent->mStateFlags &= ~Accessible::eKidsMutating;
+
+  uint32_t length = mParent->mChildren.Length();
+#ifdef DEBUG
+  for (uint32_t idx = 0; idx < mStartIdx && idx < length; idx++) {
+    MOZ_ASSERT(mParent->mChildren[idx]->mIndexInParent == static_cast<int32_t>(idx),
+               "Wrong index detected");
+  }
+#endif
+
+  for (uint32_t idx = mStartIdx; idx < length; idx++) {
+    mParent->mChildren[idx]->mIndexInParent = idx;
+    mParent->mChildren[idx]->mStateFlags |= Accessible::eGroupInfoDirty;
+  }
+
+  if (mStartIdx < mParent->mChildren.Length() - 1) {
+    mParent->mEmbeddedObjCollector = nullptr;
+  }
+
+  mParent->mStateFlags |= mStateFlagsCopy & Accessible::eKidsMutating;
+}
--- a/accessible/generic/Accessible.h
+++ b/accessible/generic/Accessible.h
@@ -1028,17 +1028,17 @@ protected:
    */
   enum StateFlags {
     eIsDefunct = 1 << 0, // accessible is defunct
     eIsNotInDocument = 1 << 1, // accessible is not in document
     eSharedNode = 1 << 2, // accessible shares DOM node from another accessible
     eNotNodeMapEntry = 1 << 3, // accessible shouldn't be in document node map
     eHasNumericValue = 1 << 4, // accessible has a numeric value
     eGroupInfoDirty = 1 << 5, // accessible needs to update group info
-    eSubtreeMutating = 1 << 6, // subtree is being mutated
+    eKidsMutating = 1 << 6, // subtree is being mutated
     eIgnoreDOMUIEvent = 1 << 7, // don't process DOM UI events for a11y events
     eSurvivingInUpdate = 1 << 8, // parent drops children to recollect them
     eRelocated = 1 << 9, // accessible was moved in tree
     eNoXBLKids = 1 << 10, // accessible don't allows XBL children
     eNoKidsFromDOM = 1 << 11, // accessible doesn't allow children from DOM
 
     eLastStateFlag = eNoKidsFromDOM
   };
@@ -1126,32 +1126,16 @@ protected:
    */
   uint32_t GetActionRule() const;
 
   /**
    * Return group info.
    */
   AccGroupInfo* GetGroupInfo();
 
-  /**
-   * Set dirty state of the accessible's group info.
-   */
-  inline void SetDirtyGroupInfo(bool aIsDirty)
-  {
-    if (aIsDirty)
-      mStateFlags |= eGroupInfoDirty;
-    else
-      mStateFlags &= ~eGroupInfoDirty;
-  }
-
-  /**
-   * Flag all children group info as needing to be updated.
-   */
-  void InvalidateChildrenGroupInfo();
-
   // Data Members
   nsCOMPtr<nsIContent> mContent;
   DocAccessible* mDoc;
 
   RefPtr<Accessible> mParent;
   nsTArray<RefPtr<Accessible> > mChildren;
   int32_t mIndexInParent;
 
@@ -1166,17 +1150,16 @@ protected:
    */
   uint32_t mChildrenFlags : kChildrenFlagsBits;
   uint32_t mStateFlags : kStateFlagsBits;
   uint32_t mContextFlags : kContextFlagsBits;
   uint32_t mType : kTypeBits;
   uint32_t mGenericTypes : kGenericTypesBits;
 
   void StaticAsserts() const;
-  void AssertInMutatingSubtree() const;
 
   friend class DocAccessible;
   friend class xpcAccessible;
   friend class AutoTreeMutation;
 
   nsAutoPtr<mozilla::a11y::EmbeddedObjCollector> mEmbeddedObjCollector;
   union {
     int32_t mIndexOfEmbeddedChild;
@@ -1262,41 +1245,49 @@ public:
 private:
   void ToPlatformFormat(nsAString& aValue) const;
   void ToAtkFormat(nsAString& aValue) const;
 
   uint32_t mKey;
   uint32_t mModifierMask;
 };
 
+
 /**
  * This class makes sure required tasks are done before and after tree
  * mutations. Currently this only includes group info invalidation. You must
  * have an object of this class on the stack when calling methods that mutate
  * the accessible tree.
  */
 class AutoTreeMutation
 {
 public:
-  explicit AutoTreeMutation(Accessible* aRoot, bool aInvalidationRequired = true) :
-    mInvalidationRequired(aInvalidationRequired), mRoot(aRoot)
+  explicit AutoTreeMutation(Accessible* aParent) :
+    mParent(aParent), mStartIdx(UINT32_MAX),
+    mStateFlagsCopy(mParent->mStateFlags)
   {
-    MOZ_ASSERT(!(mRoot->mStateFlags & Accessible::eSubtreeMutating));
-    mRoot->mStateFlags |= Accessible::eSubtreeMutating;
+    mParent->mStateFlags |= Accessible::eKidsMutating;
   }
-  ~AutoTreeMutation()
-  {
-    if (mInvalidationRequired)
-      mRoot->InvalidateChildrenGroupInfo();
 
-    MOZ_ASSERT(mRoot->mStateFlags & Accessible::eSubtreeMutating);
-    mRoot->mStateFlags &= ~Accessible::eSubtreeMutating;
+  void AfterInsertion(const Accessible* aChild) {
+    if (static_cast<uint32_t>(aChild->IndexInParent()) < mStartIdx) {
+      mStartIdx = aChild->IndexInParent() + 1;
+    }
   }
 
-  bool mInvalidationRequired;
+  void BeforeRemoval(const Accessible* aChild) {
+    if (static_cast<uint32_t>(aChild->IndexInParent()) < mStartIdx) {
+      mStartIdx = aChild->IndexInParent();
+    }
+  }
+
+  void Done();
+
 private:
-  Accessible* mRoot;
+  Accessible* mParent;
+  uint32_t mStartIdx;
+  uint32_t mStateFlagsCopy;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -493,23 +493,17 @@ DocAccessible::Shutdown()
     mVirtualCursor = nullptr;
   }
 
   mPresShell->SetDocAccessible(nullptr);
   mPresShell = nullptr;  // Avoid reentrancy
 
   mDependentIDsHash.Clear();
   mNodeToAccessibleMap.Clear();
-
-  {
-    // We're about to get rid of all of our children so there won't be anything
-    // to invalidate.
-    AutoTreeMutation mut(this, false);
-    ClearCache(mAccessibleCache);
-  }
+  ClearCache(mAccessibleCache);
 
   HyperTextAccessibleWrap::Shutdown();
 
   GetAccService()->NotifyOfDocumentShutdown(this, kungFuDeathGripDoc);
 }
 
 nsIFrame*
 DocAccessible::GetFrame() const
@@ -1391,20 +1385,24 @@ DocAccessible::ProcessInvalidationList()
     if (!HasAccessible(content)) {
       Accessible* container = GetContainerAccessible(content);
       if (container) {
         TreeWalker walker(container);
         if (container->IsAcceptableChild(content) && walker.Seek(content)) {
           Accessible* child =
             GetAccService()->GetOrCreateAccessible(content, container);
           if (child) {
-            AutoTreeMutation mMut(container);
             RefPtr<AccReorderEvent> reorderEvent =
               new AccReorderEvent(container);
+
+            AutoTreeMutation mt(container);
             container->InsertAfter(child, walker.Prev());
+            mt.AfterInsertion(child);
+            mt.Done();
+
             uint32_t flags = UpdateTreeInternal(child, true, reorderEvent);
             FireEventsOnInsertion(container, reorderEvent, flags);
           }
         }
       }
     }
   }
 
@@ -1470,19 +1468,17 @@ DocAccessible::DoInitialUpdate()
   if (nsCoreUtils::IsTabDocument(mDocumentNode))
     mDocFlags |= eTabDocument;
 
   mLoadState |= eTreeConstructed;
 
   // Set up a root element and ARIA role mapping.
   UpdateRootElIfNeeded();
 
-  // Build initial tree.  Since its the initial tree there's no group info to
-  // invalidate.
-  AutoTreeMutation mut(this, false);
+  // Build initial tree.
   CacheChildrenInSubtree(this);
 
   // Fire reorder event after the document tree is constructed. Note, since
   // this reorder event is processed by parent document then events targeted to
   // this document may be fired prior to this reorder event. If this is
   // a problem then consider to keep event processing per tab document.
   if (!IsRoot()) {
     RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(Parent());
@@ -1795,17 +1791,17 @@ DocAccessible::ProcessContentInserted(Ac
     return;
 
   // If new root content has been inserted then update it.
   if (aContainer == this) {
     UpdateRootElIfNeeded();
   }
 
   uint32_t updateFlags = 0;
-  AutoTreeMutation mut(aContainer);
+  AutoTreeMutation mt(aContainer);
   RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(aContainer);
 
 #ifdef A11Y_LOG
   logging::TreeInfo("children before insertion", logging::eVerbose,
                     aContainer);
 #endif
 
   InsertIterator iter(aContainer, aNodes);
@@ -1830,22 +1826,24 @@ DocAccessible::ProcessContentInserted(Ac
     }
 
     if (aContainer->InsertAfter(iter.Child(), iter.ChildBefore())) {
 #ifdef A11Y_LOG
       logging::TreeInfo("accessible was inserted", 0,
                         "container", aContainer, "child", iter.Child(), nullptr);
 #endif
 
+      mt.AfterInsertion(iter.Child());
       updateFlags |= UpdateTreeInternal(iter.Child(), true, reorderEvent);
       continue;
     }
 
     MOZ_ASSERT_UNREACHABLE("accessible was rejected");
   }
+  mt.Done();
 
 #ifdef A11Y_LOG
   logging::TreeInfo("children after insertion", logging::eVerbose,
                     aContainer);
 #endif
 
   FireEventsOnInsertion(aContainer, reorderEvent, updateFlags);
 }
@@ -1894,26 +1892,29 @@ DocAccessible::UpdateTreeOnRemoval(Acces
       logging::MsgEntry("child accessible: null");
 
     logging::MsgEnd();
   }
 #endif
 
   uint32_t updateFlags = eNoAccessible;
   RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(aContainer);
-  AutoTreeMutation mut(aContainer);
+  AutoTreeMutation mt(aContainer);
 
   if (child) {
+    mt.BeforeRemoval(child);
     updateFlags |= UpdateTreeInternal(child, false, reorderEvent);
   } else {
     TreeWalker walker(aContainer, aChildNode, TreeWalker::eWalkCache);
     while (Accessible* child = walker.Next()) {
+      mt.BeforeRemoval(child);
       updateFlags |= UpdateTreeInternal(child, false, reorderEvent);
     }
   }
+  mt.Done();
 
   // Content insertion/removal is not cause of accessible tree change.
   if (updateFlags == eNoAccessible)
     return;
 
   MaybeNotifyOfValueChange(aContainer);
   FireDelayedEvent(reorderEvent);
 }
@@ -2184,17 +2185,16 @@ DocAccessible::MoveChild(Accessible* aCh
   if (curParent == aNewParent) {
     MOZ_ASSERT(aChild->IndexInParent() != aIdxInParent, "No move case");
 
     RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(curParent);
     RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(aChild, false);
     reorderEvent->AddSubMutationEvent(hideEvent);
     FireDelayedEvent(hideEvent);
 
-    AutoTreeMutation mut(curParent);
     curParent->MoveChild(aIdxInParent, aChild);
 
     RefPtr<AccMutationEvent> showEvent = new AccShowEvent(aChild);
     reorderEvent->AddSubMutationEvent(showEvent);
     FireDelayedEvent(showEvent);
 
     MaybeNotifyOfValueChange(curParent);
     FireDelayedEvent(reorderEvent);
@@ -2210,33 +2210,33 @@ DocAccessible::MoveChild(Accessible* aCh
     return false;
   }
 
   RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(curParent);
   RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(aChild, false);
   reorderEvent->AddSubMutationEvent(hideEvent);
   FireDelayedEvent(hideEvent);
 
-  {
-    AutoTreeMutation mut(curParent);
-    curParent->RemoveChild(aChild);
-  }
+  AutoTreeMutation rmut(curParent);
+  rmut.BeforeRemoval(aChild);
+  curParent->RemoveChild(aChild);
+  rmut.Done();
 
   MaybeNotifyOfValueChange(curParent);
   FireDelayedEvent(reorderEvent);
 
   // No insertion point for the child.
   if (aIdxInParent == -1) {
     return true;
   }
 
-  {
-    AutoTreeMutation mut(aNewParent);
-    aNewParent->InsertChildAt(aIdxInParent, aChild);
-  }
+  AutoTreeMutation imut(aNewParent);
+  aNewParent->InsertChildAt(aIdxInParent, aChild);
+  imut.AfterInsertion(aChild);
+  imut.Done();
 
   reorderEvent = new AccReorderEvent(aNewParent);
   RefPtr<AccMutationEvent> showEvent = new AccShowEvent(aChild);
   reorderEvent->AddSubMutationEvent(showEvent);
   FireDelayedEvent(showEvent);
 
   MaybeNotifyOfValueChange(aNewParent);
   FireDelayedEvent(reorderEvent);
--- a/accessible/html/HTMLImageMapAccessible.cpp
+++ b/accessible/html/HTMLImageMapAccessible.cpp
@@ -83,31 +83,32 @@ HTMLImageMapAccessible::UpdateChildAreas
   nsImageFrame* imageFrame = do_QueryFrame(mContent->GetPrimaryFrame());
 
   // If image map is not initialized yet then we trigger one time more later.
   nsImageMap* imageMapObj = imageFrame->GetExistingImageMap();
   if (!imageMapObj)
     return;
 
   bool treeChanged = false;
-  AutoTreeMutation mut(this);
+  AutoTreeMutation mt(this);
   RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(this);
 
   // Remove areas that are not a valid part of the image map anymore.
   for (int32_t childIdx = mChildren.Length() - 1; childIdx >= 0; childIdx--) {
     Accessible* area = mChildren.ElementAt(childIdx);
     if (area->GetContent()->GetPrimaryFrame())
       continue;
 
     if (aDoFireEvents) {
       RefPtr<AccHideEvent> event = new AccHideEvent(area, area->GetContent());
       mDoc->FireDelayedEvent(event);
       reorderEvent->AddSubMutationEvent(event);
     }
 
+    mt.BeforeRemoval(area);
     RemoveChild(area);
     treeChanged = true;
   }
 
   // Insert new areas into the tree.
   uint32_t areaElmCount = imageMapObj->AreaCount();
   for (uint32_t idx = 0; idx < areaElmCount; idx++) {
     nsIContent* areaContent = imageMapObj->GetAreaAt(idx);
@@ -116,32 +117,33 @@ HTMLImageMapAccessible::UpdateChildAreas
       RefPtr<Accessible> area = new HTMLAreaAccessible(areaContent, mDoc);
       mDoc->BindToDocument(area, aria::GetRoleMap(areaContent->AsElement()));
 
       if (!InsertChildAt(idx, area)) {
         mDoc->UnbindFromDocument(area);
         break;
       }
 
+      mt.AfterInsertion(area);
+
       if (aDoFireEvents) {
         RefPtr<AccShowEvent> event = new AccShowEvent(area);
         mDoc->FireDelayedEvent(event);
         reorderEvent->AddSubMutationEvent(event);
       }
 
       treeChanged = true;
     }
   }
 
+  mt.Done();
+
   // Fire reorder event if needed.
   if (treeChanged && aDoFireEvents)
     mDoc->FireDelayedEvent(reorderEvent);
-
-  if (!treeChanged)
-    mut.mInvalidationRequired = false;
 }
 
 Accessible*
 HTMLImageMapAccessible::GetChildAccessibleFor(const nsINode* aNode) const
 {
   uint32_t length = mChildren.Length();
   for (uint32_t i = 0; i < length; i++) {
     Accessible* area = mChildren[i];
--- a/accessible/html/HTMLListAccessible.cpp
+++ b/accessible/html/HTMLListAccessible.cpp
@@ -98,35 +98,38 @@ void
 HTMLLIAccessible::UpdateBullet(bool aHasBullet)
 {
   if (aHasBullet == !!mBullet) {
     NS_NOTREACHED("Bullet and accessible are in sync already!");
     return;
   }
 
   RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(this);
-  AutoTreeMutation mut(this);
+  AutoTreeMutation mt(this);
 
   DocAccessible* document = Document();
   if (aHasBullet) {
     mBullet = new HTMLListBulletAccessible(mContent, mDoc);
     document->BindToDocument(mBullet, nullptr);
     InsertChildAt(0, mBullet);
+    mt.AfterInsertion(mBullet);
 
     RefPtr<AccShowEvent> event = new AccShowEvent(mBullet);
     mDoc->FireDelayedEvent(event);
     reorderEvent->AddSubMutationEvent(event);
   } else {
     RefPtr<AccHideEvent> event = new AccHideEvent(mBullet, mBullet->GetContent());
     mDoc->FireDelayedEvent(event);
     reorderEvent->AddSubMutationEvent(event);
 
+    mt.BeforeRemoval(mBullet);
     RemoveChild(mBullet);
     mBullet = nullptr;
   }
+  mt.Done();
 
   mDoc->FireDelayedEvent(reorderEvent);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLLIAccessible: Accessible protected
 
 void