Blame accessible/base/NotificationController.cpp

Packit f0b94e
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
Packit f0b94e
/* This Source Code Form is subject to the terms of the Mozilla Public
Packit f0b94e
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit f0b94e
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit f0b94e
Packit f0b94e
#include "NotificationController.h"
Packit f0b94e
Packit f0b94e
#include "DocAccessible-inl.h"
Packit f0b94e
#include "DocAccessibleChild.h"
Packit f0b94e
#include "TextLeafAccessible.h"
Packit f0b94e
#include "TextUpdater.h"
Packit f0b94e
Packit f0b94e
#include "mozilla/dom/TabChild.h"
Packit f0b94e
#include "mozilla/dom/Element.h"
Packit f0b94e
#include "mozilla/Telemetry.h"
Packit f0b94e
Packit f0b94e
using namespace mozilla;
Packit f0b94e
using namespace mozilla::a11y;
Packit f0b94e
Packit f0b94e
////////////////////////////////////////////////////////////////////////////////
Packit f0b94e
// NotificationCollector
Packit f0b94e
////////////////////////////////////////////////////////////////////////////////
Packit f0b94e
Packit f0b94e
NotificationController::NotificationController(DocAccessible* aDocument,
Packit f0b94e
                                               nsIPresShell* aPresShell)
Packit f0b94e
    : EventQueue(aDocument),
Packit f0b94e
      mObservingState(eNotObservingRefresh),
Packit f0b94e
      mPresShell(aPresShell),
Packit f0b94e
      mEventGeneration(0) {
Packit f0b94e
#ifdef DEBUG
Packit f0b94e
  mMoveGuardOnStack = false;
Packit f0b94e
#endif
Packit f0b94e
Packit f0b94e
  // Schedule initial accessible tree construction.
Packit f0b94e
  ScheduleProcessing();
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
NotificationController::~NotificationController() {
Packit f0b94e
  NS_ASSERTION(!mDocument, "Controller wasn't shutdown properly!");
Packit f0b94e
  if (mDocument) Shutdown();
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
////////////////////////////////////////////////////////////////////////////////
Packit f0b94e
// NotificationCollector: AddRef/Release and cycle collection
Packit f0b94e
Packit f0b94e
NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(NotificationController)
Packit f0b94e
NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE(NotificationController)
Packit f0b94e
Packit f0b94e
NS_IMPL_CYCLE_COLLECTION_CLASS(NotificationController)
Packit f0b94e
Packit f0b94e
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NotificationController)
Packit f0b94e
  if (tmp->mDocument) tmp->Shutdown();
Packit f0b94e
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
Packit f0b94e
Packit f0b94e
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NotificationController)
Packit f0b94e
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHangingChildDocuments)
Packit f0b94e
  for (auto it = tmp->mContentInsertions.ConstIter(); !it.Done(); it.Next()) {
Packit f0b94e
    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mContentInsertions key");
Packit f0b94e
    cb.NoteXPCOMChild(it.Key());
Packit f0b94e
    nsTArray<nsCOMPtr<nsIContent>>* list = it.UserData();
Packit f0b94e
    for (uint32_t i = 0; i < list->Length(); i++) {
Packit f0b94e
      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mContentInsertions value item");
Packit f0b94e
      cb.NoteXPCOMChild(list->ElementAt(i));
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvents)
Packit f0b94e
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRelocations)
Packit f0b94e
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
Packit f0b94e
Packit f0b94e
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(NotificationController, AddRef)
Packit f0b94e
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(NotificationController, Release)
Packit f0b94e
Packit f0b94e
////////////////////////////////////////////////////////////////////////////////
Packit f0b94e
// NotificationCollector: public
Packit f0b94e
Packit f0b94e
void NotificationController::Shutdown() {
Packit f0b94e
  if (mObservingState != eNotObservingRefresh &&
Packit f0b94e
      mPresShell->RemoveRefreshObserver(this, FlushType::Display)) {
Packit f0b94e
    mObservingState = eNotObservingRefresh;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // Shutdown handling child documents.
Packit f0b94e
  int32_t childDocCount = mHangingChildDocuments.Length();
Packit f0b94e
  for (int32_t idx = childDocCount - 1; idx >= 0; idx--) {
Packit f0b94e
    if (!mHangingChildDocuments[idx]->IsDefunct())
Packit f0b94e
      mHangingChildDocuments[idx]->Shutdown();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  mHangingChildDocuments.Clear();
Packit f0b94e
Packit f0b94e
  mDocument = nullptr;
Packit f0b94e
  mPresShell = nullptr;
Packit f0b94e
Packit f0b94e
  mTextHash.Clear();
Packit f0b94e
  mContentInsertions.Clear();
Packit f0b94e
  mNotifications.Clear();
Packit f0b94e
  mEvents.Clear();
Packit f0b94e
  mRelocations.Clear();
Packit f0b94e
  mEventTree.Clear();
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
EventTree* NotificationController::QueueMutation(Accessible* aContainer) {
Packit f0b94e
  EventTree* tree = mEventTree.FindOrInsert(aContainer);
Packit f0b94e
  if (tree) {
Packit f0b94e
    ScheduleProcessing();
Packit f0b94e
  }
Packit f0b94e
  return tree;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
bool NotificationController::QueueMutationEvent(AccTreeMutationEvent* aEvent) {
Packit f0b94e
  // We have to allow there to be a hide and then a show event for a target
Packit f0b94e
  // because of targets getting moved.  However we need to coalesce a show and
Packit f0b94e
  // then a hide for a target which means we need to check for that here.
Packit f0b94e
  if (aEvent->GetEventType() == nsIAccessibleEvent::EVENT_HIDE &&
Packit f0b94e
      aEvent->GetAccessible()->ShowEventTarget()) {
Packit f0b94e
    AccTreeMutationEvent* showEvent =
Packit f0b94e
        mMutationMap.GetEvent(aEvent->GetAccessible(), EventMap::ShowEvent);
Packit f0b94e
    DropMutationEvent(showEvent);
Packit f0b94e
    return false;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  AccMutationEvent* mutEvent = downcast_accEvent(aEvent);
Packit f0b94e
  mEventGeneration++;
Packit f0b94e
  mutEvent->SetEventGeneration(mEventGeneration);
Packit f0b94e
Packit f0b94e
  if (!mFirstMutationEvent) {
Packit f0b94e
    mFirstMutationEvent = aEvent;
Packit f0b94e
    ScheduleProcessing();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (mLastMutationEvent) {
Packit f0b94e
    NS_ASSERTION(!mLastMutationEvent->NextEvent(),
Packit f0b94e
                 "why isn't the last event the end?");
Packit f0b94e
    mLastMutationEvent->SetNextEvent(aEvent);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  aEvent->SetPrevEvent(mLastMutationEvent);
Packit f0b94e
  mLastMutationEvent = aEvent;
Packit f0b94e
  mMutationMap.PutEvent(aEvent);
Packit f0b94e
Packit f0b94e
  // Because we could be hiding the target of a show event we need to get rid
Packit f0b94e
  // of any such events.  It may be possible to do less than coallesce all
Packit f0b94e
  // events, however that is easiest.
Packit f0b94e
  if (aEvent->GetEventType() == nsIAccessibleEvent::EVENT_HIDE) {
Packit f0b94e
    CoalesceMutationEvents();
Packit f0b94e
Packit f0b94e
    // mLastMutationEvent will point to something other than aEvent if and only
Packit f0b94e
    // if aEvent was just coalesced away.  In that case a parent accessible
Packit f0b94e
    // must already have the required reorder and text change events so we are
Packit f0b94e
    // done here.
Packit f0b94e
    if (mLastMutationEvent != aEvent) {
Packit f0b94e
      return false;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // We need to fire a reorder event after all of the events targeted at shown
Packit f0b94e
  // or hidden children of a container.  So either queue a new one, or move an
Packit f0b94e
  // existing one to the end of the queue if the container already has a
Packit f0b94e
  // reorder event.
Packit f0b94e
  Accessible* target = aEvent->GetAccessible();
Packit f0b94e
  Accessible* container = aEvent->GetAccessible()->Parent();
Packit f0b94e
  RefPtr<AccReorderEvent> reorder;
Packit f0b94e
  if (!container->ReorderEventTarget()) {
Packit f0b94e
    reorder = new AccReorderEvent(container);
Packit f0b94e
    container->SetReorderEventTarget(true);
Packit f0b94e
    mMutationMap.PutEvent(reorder);
Packit f0b94e
Packit f0b94e
    // Since this is the first child of container that is changing, the name of
Packit f0b94e
    // container may be changing.
Packit f0b94e
    QueueNameChange(target);
Packit f0b94e
  } else {
Packit f0b94e
    AccReorderEvent* event = downcast_accEvent(
Packit f0b94e
        mMutationMap.GetEvent(container, EventMap::ReorderEvent));
Packit f0b94e
    reorder = event;
Packit f0b94e
    if (mFirstMutationEvent == event) {
Packit f0b94e
      mFirstMutationEvent = event->NextEvent();
Packit f0b94e
    } else {
Packit f0b94e
      event->PrevEvent()->SetNextEvent(event->NextEvent());
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    event->NextEvent()->SetPrevEvent(event->PrevEvent());
Packit f0b94e
    event->SetNextEvent(nullptr);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  reorder->SetEventGeneration(mEventGeneration);
Packit f0b94e
  reorder->SetPrevEvent(mLastMutationEvent);
Packit f0b94e
  mLastMutationEvent->SetNextEvent(reorder);
Packit f0b94e
  mLastMutationEvent = reorder;
Packit f0b94e
Packit f0b94e
  // It is not possible to have a text change event for something other than a
Packit f0b94e
  // hyper text accessible.
Packit f0b94e
  if (!container->IsHyperText()) {
Packit f0b94e
    return true;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  MOZ_ASSERT(mutEvent);
Packit f0b94e
Packit f0b94e
  nsString text;
Packit f0b94e
  aEvent->GetAccessible()->AppendTextTo(text);
Packit f0b94e
  if (text.IsEmpty()) {
Packit f0b94e
    return true;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  int32_t offset = container->AsHyperText()->GetChildOffset(target);
Packit f0b94e
  AccTreeMutationEvent* prevEvent = aEvent->PrevEvent();
Packit f0b94e
  while (prevEvent &&
Packit f0b94e
         prevEvent->GetEventType() == nsIAccessibleEvent::EVENT_REORDER) {
Packit f0b94e
    prevEvent = prevEvent->PrevEvent();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (prevEvent &&
Packit f0b94e
      prevEvent->GetEventType() == nsIAccessibleEvent::EVENT_HIDE &&
Packit f0b94e
      mutEvent->IsHide()) {
Packit f0b94e
    AccHideEvent* prevHide = downcast_accEvent(prevEvent);
Packit f0b94e
    AccTextChangeEvent* prevTextChange = prevHide->mTextChangeEvent;
Packit f0b94e
    if (prevTextChange && prevHide->Parent() == mutEvent->Parent()) {
Packit f0b94e
      if (prevHide->mNextSibling == target) {
Packit f0b94e
        target->AppendTextTo(prevTextChange->mModifiedText);
Packit f0b94e
        prevHide->mTextChangeEvent.swap(mutEvent->mTextChangeEvent);
Packit f0b94e
      } else if (prevHide->mPrevSibling == target) {
Packit f0b94e
        nsString temp;
Packit f0b94e
        target->AppendTextTo(temp);
Packit f0b94e
Packit f0b94e
        uint32_t extraLen = temp.Length();
Packit f0b94e
        temp += prevTextChange->mModifiedText;
Packit f0b94e
        ;
Packit f0b94e
        prevTextChange->mModifiedText = temp;
Packit f0b94e
        prevTextChange->mStart -= extraLen;
Packit f0b94e
        prevHide->mTextChangeEvent.swap(mutEvent->mTextChangeEvent);
Packit f0b94e
      }
Packit f0b94e
    }
Packit f0b94e
  } else if (prevEvent && mutEvent->IsShow() &&
Packit f0b94e
             prevEvent->GetEventType() == nsIAccessibleEvent::EVENT_SHOW) {
Packit f0b94e
    AccShowEvent* prevShow = downcast_accEvent(prevEvent);
Packit f0b94e
    AccTextChangeEvent* prevTextChange = prevShow->mTextChangeEvent;
Packit f0b94e
    if (prevTextChange && prevShow->Parent() == target->Parent()) {
Packit f0b94e
      int32_t index = target->IndexInParent();
Packit f0b94e
      int32_t prevIndex = prevShow->GetAccessible()->IndexInParent();
Packit f0b94e
      if (prevIndex + 1 == index) {
Packit f0b94e
        target->AppendTextTo(prevTextChange->mModifiedText);
Packit f0b94e
        prevShow->mTextChangeEvent.swap(mutEvent->mTextChangeEvent);
Packit f0b94e
      } else if (index + 1 == prevIndex) {
Packit f0b94e
        nsString temp;
Packit f0b94e
        target->AppendTextTo(temp);
Packit f0b94e
        prevTextChange->mStart -= temp.Length();
Packit f0b94e
        temp += prevTextChange->mModifiedText;
Packit f0b94e
        prevTextChange->mModifiedText = temp;
Packit f0b94e
        prevShow->mTextChangeEvent.swap(mutEvent->mTextChangeEvent);
Packit f0b94e
      }
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (!mutEvent->mTextChangeEvent) {
Packit f0b94e
    mutEvent->mTextChangeEvent = new AccTextChangeEvent(
Packit f0b94e
        container, offset, text, mutEvent->IsShow(),
Packit f0b94e
        aEvent->mIsFromUserInput ? eFromUserInput : eNoUserInput);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return true;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void NotificationController::DropMutationEvent(AccTreeMutationEvent* aEvent) {
Packit f0b94e
  // unset the event bits since the event isn't being fired any more.
Packit f0b94e
  if (aEvent->GetEventType() == nsIAccessibleEvent::EVENT_REORDER) {
Packit f0b94e
    aEvent->GetAccessible()->SetReorderEventTarget(false);
Packit f0b94e
  } else if (aEvent->GetEventType() == nsIAccessibleEvent::EVENT_SHOW) {
Packit f0b94e
    aEvent->GetAccessible()->SetShowEventTarget(false);
Packit f0b94e
  } else {
Packit f0b94e
    aEvent->GetAccessible()->SetHideEventTarget(false);
Packit f0b94e
Packit f0b94e
    AccHideEvent* hideEvent = downcast_accEvent(aEvent);
Packit f0b94e
    MOZ_ASSERT(hideEvent);
Packit f0b94e
Packit f0b94e
    if (hideEvent->NeedsShutdown()) {
Packit f0b94e
      mDocument->ShutdownChildrenInSubtree(aEvent->GetAccessible());
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // Do the work to splice the event out of the list.
Packit f0b94e
  if (mFirstMutationEvent == aEvent) {
Packit f0b94e
    mFirstMutationEvent = aEvent->NextEvent();
Packit f0b94e
  } else {
Packit f0b94e
    aEvent->PrevEvent()->SetNextEvent(aEvent->NextEvent());
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (mLastMutationEvent == aEvent) {
Packit f0b94e
    mLastMutationEvent = aEvent->PrevEvent();
Packit f0b94e
  } else {
Packit f0b94e
    aEvent->NextEvent()->SetPrevEvent(aEvent->PrevEvent());
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  aEvent->SetPrevEvent(nullptr);
Packit f0b94e
  aEvent->SetNextEvent(nullptr);
Packit f0b94e
  mMutationMap.RemoveEvent(aEvent);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void NotificationController::CoalesceMutationEvents() {
Packit f0b94e
  AccTreeMutationEvent* event = mFirstMutationEvent;
Packit f0b94e
  while (event) {
Packit f0b94e
    AccTreeMutationEvent* nextEvent = event->NextEvent();
Packit f0b94e
    uint32_t eventType = event->GetEventType();
Packit f0b94e
    if (event->GetEventType() == nsIAccessibleEvent::EVENT_REORDER) {
Packit f0b94e
      Accessible* acc = event->GetAccessible();
Packit f0b94e
      while (acc) {
Packit f0b94e
        if (acc->IsDoc()) {
Packit f0b94e
          break;
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        // if a parent of the reorder event's target is being hidden that
Packit f0b94e
        // hide event's target must have a parent that is also a reorder event
Packit f0b94e
        // target.  That means we don't need this reorder event.
Packit f0b94e
        if (acc->HideEventTarget()) {
Packit f0b94e
          DropMutationEvent(event);
Packit f0b94e
          break;
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        Accessible* parent = acc->Parent();
Packit f0b94e
        if (parent->ReorderEventTarget()) {
Packit f0b94e
          AccReorderEvent* reorder = downcast_accEvent(
Packit f0b94e
              mMutationMap.GetEvent(parent, EventMap::ReorderEvent));
Packit f0b94e
Packit f0b94e
          // We want to make sure that a reorder event comes after any show or
Packit f0b94e
          // hide events targeted at the children of its target.  We keep the
Packit f0b94e
          // invariant that event generation goes up as you are farther in the
Packit f0b94e
          // queue, so we want to use the spot of the event with the higher
Packit f0b94e
          // generation number, and keep that generation number.
Packit f0b94e
          if (reorder &&
Packit f0b94e
              reorder->EventGeneration() < event->EventGeneration()) {
Packit f0b94e
            reorder->SetEventGeneration(event->EventGeneration());
Packit f0b94e
Packit f0b94e
            // It may be true that reorder was before event, and we coalesced
Packit f0b94e
            // away all the show / hide events between them.  In that case
Packit f0b94e
            // event is already immediately after reorder in the queue and we
Packit f0b94e
            // do not need to rearrange the list of events.
Packit f0b94e
            if (event != reorder->NextEvent()) {
Packit f0b94e
              // There really should be a show or hide event before the first
Packit f0b94e
              // reorder event.
Packit f0b94e
              if (reorder->PrevEvent()) {
Packit f0b94e
                reorder->PrevEvent()->SetNextEvent(reorder->NextEvent());
Packit f0b94e
              } else {
Packit f0b94e
                mFirstMutationEvent = reorder->NextEvent();
Packit f0b94e
              }
Packit f0b94e
Packit f0b94e
              reorder->NextEvent()->SetPrevEvent(reorder->PrevEvent());
Packit f0b94e
              event->PrevEvent()->SetNextEvent(reorder);
Packit f0b94e
              reorder->SetPrevEvent(event->PrevEvent());
Packit f0b94e
              event->SetPrevEvent(reorder);
Packit f0b94e
              reorder->SetNextEvent(event);
Packit f0b94e
            }
Packit f0b94e
          }
Packit f0b94e
          DropMutationEvent(event);
Packit f0b94e
          break;
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        acc = parent;
Packit f0b94e
      }
Packit f0b94e
    } else if (eventType == nsIAccessibleEvent::EVENT_SHOW) {
Packit f0b94e
      Accessible* parent = event->GetAccessible()->Parent();
Packit f0b94e
      while (parent) {
Packit f0b94e
        if (parent->IsDoc()) {
Packit f0b94e
          break;
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        // if the parent of a show event is being either shown or hidden then
Packit f0b94e
        // we don't need to fire a show event for a subtree of that change.
Packit f0b94e
        if (parent->ShowEventTarget() || parent->HideEventTarget()) {
Packit f0b94e
          DropMutationEvent(event);
Packit f0b94e
          break;
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        parent = parent->Parent();
Packit f0b94e
      }
Packit f0b94e
    } else {
Packit f0b94e
      MOZ_ASSERT(eventType == nsIAccessibleEvent::EVENT_HIDE,
Packit f0b94e
                 "mutation event list has an invalid event");
Packit f0b94e
Packit f0b94e
      AccHideEvent* hideEvent = downcast_accEvent(event);
Packit f0b94e
      Accessible* parent = hideEvent->Parent();
Packit f0b94e
      while (parent) {
Packit f0b94e
        if (parent->IsDoc()) {
Packit f0b94e
          break;
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        if (parent->HideEventTarget()) {
Packit f0b94e
          DropMutationEvent(event);
Packit f0b94e
          break;
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        if (parent->ShowEventTarget()) {
Packit f0b94e
          AccShowEvent* showEvent = downcast_accEvent(
Packit f0b94e
              mMutationMap.GetEvent(parent, EventMap::ShowEvent));
Packit f0b94e
          if (showEvent->EventGeneration() < hideEvent->EventGeneration()) {
Packit f0b94e
            DropMutationEvent(hideEvent);
Packit f0b94e
            break;
Packit f0b94e
          }
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        parent = parent->Parent();
Packit f0b94e
      }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    event = nextEvent;
Packit f0b94e
  }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void NotificationController::ScheduleChildDocBinding(DocAccessible* aDocument) {
Packit f0b94e
  // Schedule child document binding to the tree.
Packit f0b94e
  mHangingChildDocuments.AppendElement(aDocument);
Packit f0b94e
  ScheduleProcessing();
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void NotificationController::ScheduleContentInsertion(
Packit f0b94e
    Accessible* aContainer, nsIContent* aStartChildNode,
Packit f0b94e
    nsIContent* aEndChildNode) {
Packit f0b94e
  nsTArray<nsCOMPtr<nsIContent>> list;
Packit f0b94e
Packit f0b94e
  bool needsProcessing = false;
Packit f0b94e
  nsIContent* node = aStartChildNode;
Packit f0b94e
  while (node != aEndChildNode) {
Packit f0b94e
    // Notification triggers for content insertion even if no content was
Packit f0b94e
    // actually inserted, check if the given content has a frame to discard
Packit f0b94e
    // this case early.
Packit f0b94e
    if (node->GetPrimaryFrame()) {
Packit f0b94e
      if (list.AppendElement(node)) needsProcessing = true;
Packit f0b94e
    }
Packit f0b94e
    node = node->GetNextSibling();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (needsProcessing) {
Packit f0b94e
    mContentInsertions.LookupOrAdd(aContainer)->AppendElements(list);
Packit f0b94e
    ScheduleProcessing();
Packit f0b94e
  }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void NotificationController::ScheduleProcessing() {
Packit f0b94e
  // If notification flush isn't planed yet start notification flush
Packit f0b94e
  // asynchronously (after style and layout).
Packit f0b94e
  if (mObservingState == eNotObservingRefresh) {
Packit f0b94e
    if (mPresShell->AddRefreshObserver(this, FlushType::Display))
Packit f0b94e
      mObservingState = eRefreshObserving;
Packit f0b94e
  }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
////////////////////////////////////////////////////////////////////////////////
Packit f0b94e
// NotificationCollector: protected
Packit f0b94e
Packit f0b94e
bool NotificationController::IsUpdatePending() {
Packit f0b94e
  return mPresShell->IsLayoutFlushObserver() ||
Packit f0b94e
         mObservingState == eRefreshProcessingForUpdate || WaitingForParent() ||
Packit f0b94e
         mContentInsertions.Count() != 0 || mNotifications.Length() != 0 ||
Packit f0b94e
         mTextHash.Count() != 0 ||
Packit f0b94e
         !mDocument->HasLoadState(DocAccessible::eTreeConstructed);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
bool NotificationController::WaitingForParent() {
Packit f0b94e
  DocAccessible* parentdoc = mDocument->ParentDocument();
Packit f0b94e
  if (!parentdoc) {
Packit f0b94e
    return false;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  NotificationController* parent = parentdoc->mNotificationController;
Packit f0b94e
  if (!parent || parent == this) {
Packit f0b94e
    // Do not wait for nothing or ourselves
Packit f0b94e
    return false;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // Wait for parent's notifications processing
Packit f0b94e
  return parent->mContentInsertions.Count() != 0 ||
Packit f0b94e
         parent->mNotifications.Length() != 0;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void NotificationController::ProcessMutationEvents() {
Packit f0b94e
  // there is no reason to fire a hide event for a child of a show event
Packit f0b94e
  // target.  That can happen if something is inserted into the tree and
Packit f0b94e
  // removed before the next refresh driver tick, but it should not be
Packit f0b94e
  // observable outside gecko so it should be safe to coalesce away any such
Packit f0b94e
  // events.  This means that it should be fine to fire all of the hide events
Packit f0b94e
  // first, and then deal with any shown subtrees.
Packit f0b94e
  for (AccTreeMutationEvent* event = mFirstMutationEvent; event;
Packit f0b94e
       event = event->NextEvent()) {
Packit f0b94e
    if (event->GetEventType() != nsIAccessibleEvent::EVENT_HIDE) {
Packit f0b94e
      continue;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    nsEventShell::FireEvent(event);
Packit f0b94e
    if (!mDocument) {
Packit f0b94e
      return;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    AccMutationEvent* mutEvent = downcast_accEvent(event);
Packit f0b94e
    if (mutEvent->mTextChangeEvent) {
Packit f0b94e
      nsEventShell::FireEvent(mutEvent->mTextChangeEvent);
Packit f0b94e
      if (!mDocument) {
Packit f0b94e
        return;
Packit f0b94e
      }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    // Fire menupopup end event before a hide event if a menu goes away.
Packit f0b94e
Packit f0b94e
    // XXX: We don't look into children of hidden subtree to find hiding
Packit f0b94e
    // menupopup (as we did prior bug 570275) because we don't do that when
Packit f0b94e
    // menu is showing (and that's impossible until bug 606924 is fixed).
Packit f0b94e
    // Nevertheless we should do this at least because layout coalesces
Packit f0b94e
    // the changes before our processing and we may miss some menupopup
Packit f0b94e
    // events. Now we just want to be consistent in content insertion/removal
Packit f0b94e
    // handling.
Packit f0b94e
    if (event->mAccessible->ARIARole() == roles::MENUPOPUP) {
Packit f0b94e
      nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
Packit f0b94e
                              event->mAccessible);
Packit f0b94e
      if (!mDocument) {
Packit f0b94e
        return;
Packit f0b94e
      }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    AccHideEvent* hideEvent = downcast_accEvent(event);
Packit f0b94e
    if (hideEvent->NeedsShutdown()) {
Packit f0b94e
      mDocument->ShutdownChildrenInSubtree(event->mAccessible);
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // Group the show events by the parent of their target.
Packit f0b94e
  nsDataHashtable<nsPtrHashKey<Accessible>, nsTArray<AccTreeMutationEvent*>>
Packit f0b94e
      showEvents;
Packit f0b94e
  for (AccTreeMutationEvent* event = mFirstMutationEvent; event;
Packit f0b94e
       event = event->NextEvent()) {
Packit f0b94e
    if (event->GetEventType() != nsIAccessibleEvent::EVENT_SHOW) {
Packit f0b94e
      continue;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    Accessible* parent = event->GetAccessible()->Parent();
Packit f0b94e
    showEvents.GetOrInsert(parent).AppendElement(event);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // We need to fire show events for the children of an accessible in the order
Packit f0b94e
  // of their indices at this point.  So sort each set of events for the same
Packit f0b94e
  // container by the index of their target.
Packit f0b94e
  for (auto iter = showEvents.Iter(); !iter.Done(); iter.Next()) {
Packit f0b94e
    struct AccIdxComparator {
Packit f0b94e
      bool LessThan(const AccTreeMutationEvent* a,
Packit f0b94e
                    const AccTreeMutationEvent* b) const {
Packit f0b94e
        int32_t aIdx = a->GetAccessible()->IndexInParent();
Packit f0b94e
        int32_t bIdx = b->GetAccessible()->IndexInParent();
Packit f0b94e
        MOZ_ASSERT(aIdx >= 0 && bIdx >= 0 && aIdx != bIdx);
Packit f0b94e
        return aIdx < bIdx;
Packit f0b94e
      }
Packit f0b94e
      bool Equals(const AccTreeMutationEvent* a,
Packit f0b94e
                  const AccTreeMutationEvent* b) const {
Packit f0b94e
        DebugOnly<int32_t> aIdx = a->GetAccessible()->IndexInParent();
Packit f0b94e
        DebugOnly<int32_t> bIdx = b->GetAccessible()->IndexInParent();
Packit f0b94e
        MOZ_ASSERT(aIdx >= 0 && bIdx >= 0 && aIdx != bIdx);
Packit f0b94e
        return false;
Packit f0b94e
      }
Packit f0b94e
    };
Packit f0b94e
Packit f0b94e
    nsTArray<AccTreeMutationEvent*>& events = iter.Data();
Packit f0b94e
    events.Sort(AccIdxComparator());
Packit f0b94e
    for (AccTreeMutationEvent* event : events) {
Packit f0b94e
      nsEventShell::FireEvent(event);
Packit f0b94e
      if (!mDocument) {
Packit f0b94e
        return;
Packit f0b94e
      }
Packit f0b94e
Packit f0b94e
      AccMutationEvent* mutEvent = downcast_accEvent(event);
Packit f0b94e
      if (mutEvent->mTextChangeEvent) {
Packit f0b94e
        nsEventShell::FireEvent(mutEvent->mTextChangeEvent);
Packit f0b94e
        if (!mDocument) {
Packit f0b94e
          return;
Packit f0b94e
        }
Packit f0b94e
      }
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // Now we can fire the reorder events after all the show and hide events.
Packit f0b94e
  for (AccTreeMutationEvent* event = mFirstMutationEvent; event;
Packit f0b94e
       event = event->NextEvent()) {
Packit f0b94e
    if (event->GetEventType() != nsIAccessibleEvent::EVENT_REORDER) {
Packit f0b94e
      continue;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    nsEventShell::FireEvent(event);
Packit f0b94e
    if (!mDocument) {
Packit f0b94e
      return;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    Accessible* target = event->GetAccessible();
Packit f0b94e
    target->Document()->MaybeNotifyOfValueChange(target);
Packit f0b94e
    if (!mDocument) {
Packit f0b94e
      return;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
////////////////////////////////////////////////////////////////////////////////
Packit f0b94e
// NotificationCollector: private
Packit f0b94e
Packit f0b94e
void NotificationController::WillRefresh(mozilla::TimeStamp aTime) {
Packit f0b94e
  Telemetry::AutoTimer<Telemetry::A11Y_TREE_UPDATE_TIMING_MS> timer;
Packit f0b94e
Packit f0b94e
  AUTO_PROFILER_LABEL("NotificationController::WillRefresh", OTHER);
Packit f0b94e
Packit f0b94e
  // If the document accessible that notification collector was created for is
Packit f0b94e
  // now shut down, don't process notifications anymore.
Packit f0b94e
  NS_ASSERTION(
Packit f0b94e
      mDocument,
Packit f0b94e
      "The document was shut down while refresh observer is attached!");
Packit f0b94e
  if (!mDocument) return;
Packit f0b94e
Packit f0b94e
  // Wait until an update, we have started, or an interruptible reflow is
Packit f0b94e
  // finished.
Packit f0b94e
  if (mObservingState == eRefreshProcessing ||
Packit f0b94e
      mObservingState == eRefreshProcessingForUpdate ||
Packit f0b94e
      mPresShell->IsReflowInterrupted()) {
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // Process parent's notifications before ours, to get proper ordering between
Packit f0b94e
  // e.g. tab event and content event.
Packit f0b94e
  if (WaitingForParent()) {
Packit f0b94e
    mDocument->ParentDocument()->mNotificationController->WillRefresh(aTime);
Packit f0b94e
    if (!mDocument) {
Packit f0b94e
      return;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // Any generic notifications should be queued if we're processing content
Packit f0b94e
  // insertions or generic notifications.
Packit f0b94e
  mObservingState = eRefreshProcessingForUpdate;
Packit f0b94e
Packit f0b94e
  // Initial accessible tree construction.
Packit f0b94e
  if (!mDocument->HasLoadState(DocAccessible::eTreeConstructed)) {
Packit f0b94e
    // If document is not bound to parent at this point then the document is not
Packit f0b94e
    // ready yet (process notifications later).
Packit f0b94e
    if (!mDocument->IsBoundToParent()) {
Packit f0b94e
      mObservingState = eRefreshObserving;
Packit f0b94e
      return;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
#ifdef A11Y_LOG
Packit f0b94e
    if (logging::IsEnabled(logging::eTree)) {
Packit f0b94e
      logging::MsgBegin("TREE", "initial tree created");
Packit f0b94e
      logging::Address("document", mDocument);
Packit f0b94e
      logging::MsgEnd();
Packit f0b94e
    }
Packit f0b94e
#endif
Packit f0b94e
Packit f0b94e
    mDocument->DoInitialUpdate();
Packit f0b94e
Packit f0b94e
    NS_ASSERTION(mContentInsertions.Count() == 0,
Packit f0b94e
                 "Pending content insertions while initial accessible tree "
Packit f0b94e
                 "isn't created!");
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // Initialize scroll support if needed.
Packit f0b94e
  if (!(mDocument->mDocFlags & DocAccessible::eScrollInitialized))
Packit f0b94e
    mDocument->AddScrollListener();
Packit f0b94e
Packit f0b94e
  // Process rendered text change notifications.
Packit f0b94e
  for (auto iter = mTextHash.Iter(); !iter.Done(); iter.Next()) {
Packit f0b94e
    nsCOMPtrHashKey<nsIContent>* entry = iter.Get();
Packit f0b94e
    nsIContent* textNode = entry->GetKey();
Packit f0b94e
    Accessible* textAcc = mDocument->GetAccessible(textNode);
Packit f0b94e
Packit f0b94e
    // If the text node is not in tree or doesn't have a frame, or placed in
Packit f0b94e
    // another document, then this case should have been handled already by
Packit f0b94e
    // content removal notifications.
Packit f0b94e
    nsINode* containerNode = textNode->GetFlattenedTreeParentNode();
Packit f0b94e
    if (!containerNode || textNode->OwnerDoc() != mDocument->DocumentNode()) {
Packit f0b94e
      MOZ_ASSERT(!textAcc,
Packit f0b94e
                 "Text node was removed but accessible is kept alive!");
Packit f0b94e
      continue;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    nsIFrame* textFrame = textNode->GetPrimaryFrame();
Packit f0b94e
    if (!textFrame) {
Packit f0b94e
      MOZ_ASSERT(!textAcc,
Packit f0b94e
                 "Text node isn't rendered but accessible is kept alive!");
Packit f0b94e
      continue;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
#ifdef A11Y_LOG
Packit f0b94e
    nsIContent* containerElm =
Packit f0b94e
        containerNode->IsElement() ? containerNode->AsElement() : nullptr;
Packit f0b94e
#endif
Packit f0b94e
Packit f0b94e
    nsIFrame::RenderedText text = textFrame->GetRenderedText(
Packit f0b94e
        0, UINT32_MAX, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
Packit f0b94e
        nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
Packit f0b94e
Packit f0b94e
    // Remove text accessible if rendered text is empty.
Packit f0b94e
    if (textAcc) {
Packit f0b94e
      if (text.mString.IsEmpty()) {
Packit f0b94e
#ifdef A11Y_LOG
Packit f0b94e
        if (logging::IsEnabled(logging::eTree | logging::eText)) {
Packit f0b94e
          logging::MsgBegin("TREE", "text node lost its content; doc: %p",
Packit f0b94e
                            mDocument);
Packit f0b94e
          logging::Node("container", containerElm);
Packit f0b94e
          logging::Node("content", textNode);
Packit f0b94e
          logging::MsgEnd();
Packit f0b94e
        }
Packit f0b94e
#endif
Packit f0b94e
Packit f0b94e
        mDocument->ContentRemoved(textAcc);
Packit f0b94e
        continue;
Packit f0b94e
      }
Packit f0b94e
Packit f0b94e
        // Update text of the accessible and fire text change events.
Packit f0b94e
#ifdef A11Y_LOG
Packit f0b94e
      if (logging::IsEnabled(logging::eText)) {
Packit f0b94e
        logging::MsgBegin("TEXT", "text may be changed; doc: %p", mDocument);
Packit f0b94e
        logging::Node("container", containerElm);
Packit f0b94e
        logging::Node("content", textNode);
Packit f0b94e
        logging::MsgEntry(
Packit f0b94e
            "old text '%s'",
Packit f0b94e
            NS_ConvertUTF16toUTF8(textAcc->AsTextLeaf()->Text()).get());
Packit f0b94e
        logging::MsgEntry("new text: '%s'",
Packit f0b94e
                          NS_ConvertUTF16toUTF8(text.mString).get());
Packit f0b94e
        logging::MsgEnd();
Packit f0b94e
      }
Packit f0b94e
#endif
Packit f0b94e
Packit f0b94e
      TextUpdater::Run(mDocument, textAcc->AsTextLeaf(), text.mString);
Packit f0b94e
      continue;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    // Append an accessible if rendered text is not empty.
Packit f0b94e
    if (!text.mString.IsEmpty()) {
Packit f0b94e
#ifdef A11Y_LOG
Packit f0b94e
      if (logging::IsEnabled(logging::eTree | logging::eText)) {
Packit f0b94e
        logging::MsgBegin("TREE", "text node gains new content; doc: %p",
Packit f0b94e
                          mDocument);
Packit f0b94e
        logging::Node("container", containerElm);
Packit f0b94e
        logging::Node("content", textNode);
Packit f0b94e
        logging::MsgEnd();
Packit f0b94e
      }
Packit f0b94e
#endif
Packit f0b94e
Packit f0b94e
      Accessible* container =
Packit f0b94e
          mDocument->AccessibleOrTrueContainer(containerNode);
Packit f0b94e
      MOZ_ASSERT(container,
Packit f0b94e
                 "Text node having rendered text hasn't accessible document!");
Packit f0b94e
      if (container) {
Packit f0b94e
        nsTArray<nsCOMPtr<nsIContent>>* list =
Packit f0b94e
            mContentInsertions.LookupOrAdd(container);
Packit f0b94e
        list->AppendElement(textNode);
Packit f0b94e
      }
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
  mTextHash.Clear();
Packit f0b94e
Packit f0b94e
  // Process content inserted notifications to update the tree.
Packit f0b94e
  for (auto iter = mContentInsertions.ConstIter(); !iter.Done(); iter.Next()) {
Packit f0b94e
    mDocument->ProcessContentInserted(iter.Key(), iter.UserData());
Packit f0b94e
    if (!mDocument) {
Packit f0b94e
      return;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
  mContentInsertions.Clear();
Packit f0b94e
Packit f0b94e
  // Bind hanging child documents unless we are using IPC and the
Packit f0b94e
  // document has no IPC actor.  If we fail to bind the child doc then
Packit f0b94e
  // shut it down.
Packit f0b94e
  uint32_t hangingDocCnt = mHangingChildDocuments.Length();
Packit f0b94e
  nsTArray<RefPtr<DocAccessible>> newChildDocs;
Packit f0b94e
  for (uint32_t idx = 0; idx < hangingDocCnt; idx++) {
Packit f0b94e
    DocAccessible* childDoc = mHangingChildDocuments[idx];
Packit f0b94e
    if (childDoc->IsDefunct()) continue;
Packit f0b94e
Packit f0b94e
    if (IPCAccessibilityActive() && !mDocument->IPCDoc()) {
Packit f0b94e
      childDoc->Shutdown();
Packit f0b94e
      continue;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    nsIContent* ownerContent =
Packit f0b94e
        mDocument->DocumentNode()->FindContentForSubDocument(
Packit f0b94e
            childDoc->DocumentNode());
Packit f0b94e
    if (ownerContent) {
Packit f0b94e
      Accessible* outerDocAcc = mDocument->GetAccessible(ownerContent);
Packit f0b94e
      if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) {
Packit f0b94e
        if (mDocument->AppendChildDocument(childDoc)) {
Packit f0b94e
          newChildDocs.AppendElement(Move(mHangingChildDocuments[idx]));
Packit f0b94e
          continue;
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        outerDocAcc->RemoveChild(childDoc);
Packit f0b94e
      }
Packit f0b94e
Packit f0b94e
      // Failed to bind the child document, destroy it.
Packit f0b94e
      childDoc->Shutdown();
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // Clear the hanging documents list, even if we didn't bind them.
Packit f0b94e
  mHangingChildDocuments.Clear();
Packit f0b94e
  MOZ_ASSERT(mDocument, "Illicit document shutdown");
Packit f0b94e
  if (!mDocument) {
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // If the document is ready and all its subdocuments are completely loaded
Packit f0b94e
  // then process the document load.
Packit f0b94e
  if (mDocument->HasLoadState(DocAccessible::eReady) &&
Packit f0b94e
      !mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) &&
Packit f0b94e
      hangingDocCnt == 0) {
Packit f0b94e
    uint32_t childDocCnt = mDocument->ChildDocumentCount(), childDocIdx = 0;
Packit f0b94e
    for (; childDocIdx < childDocCnt; childDocIdx++) {
Packit f0b94e
      DocAccessible* childDoc = mDocument->GetChildDocumentAt(childDocIdx);
Packit f0b94e
      if (!childDoc->HasLoadState(DocAccessible::eCompletelyLoaded)) break;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    if (childDocIdx == childDocCnt) {
Packit f0b94e
      mDocument->ProcessLoad();
Packit f0b94e
      if (!mDocument) return;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // Process only currently queued generic notifications.
Packit f0b94e
  nsTArray<RefPtr<Notification>> notifications;
Packit f0b94e
  notifications.SwapElements(mNotifications);
Packit f0b94e
Packit f0b94e
  uint32_t notificationCount = notifications.Length();
Packit f0b94e
  for (uint32_t idx = 0; idx < notificationCount; idx++) {
Packit f0b94e
    notifications[idx]->Process();
Packit f0b94e
    if (!mDocument) return;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // Process invalidation list of the document after all accessible tree
Packit f0b94e
  // modification are done.
Packit f0b94e
  mDocument->ProcessInvalidationList();
Packit f0b94e
Packit f0b94e
  // Process relocation list.
Packit f0b94e
  for (uint32_t idx = 0; idx < mRelocations.Length(); idx++) {
Packit f0b94e
    // owner should be in a document and have na associated DOM node (docs
Packit f0b94e
    // sometimes don't)
Packit f0b94e
    if (mRelocations[idx]->IsInDocument() &&
Packit f0b94e
        mRelocations[idx]->HasOwnContent()) {
Packit f0b94e
      mDocument->DoARIAOwnsRelocation(mRelocations[idx]);
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
  mRelocations.Clear();
Packit f0b94e
Packit f0b94e
  // If a generic notification occurs after this point then we may be allowed to
Packit f0b94e
  // process it synchronously.  However we do not want to reenter if fireing
Packit f0b94e
  // events causes script to run.
Packit f0b94e
  mObservingState = eRefreshProcessing;
Packit f0b94e
Packit f0b94e
  CoalesceMutationEvents();
Packit f0b94e
  ProcessMutationEvents();
Packit f0b94e
  mEventGeneration = 0;
Packit f0b94e
Packit f0b94e
  // Now that we are done with them get rid of the events we fired.
Packit f0b94e
  RefPtr<AccTreeMutationEvent> mutEvent = Move(mFirstMutationEvent);
Packit f0b94e
  mLastMutationEvent = nullptr;
Packit f0b94e
  mFirstMutationEvent = nullptr;
Packit f0b94e
  while (mutEvent) {
Packit f0b94e
    RefPtr<AccTreeMutationEvent> nextEvent = mutEvent->NextEvent();
Packit f0b94e
    Accessible* target = mutEvent->GetAccessible();
Packit f0b94e
Packit f0b94e
    // We need to be careful here, while it may seem that we can simply 0 all
Packit f0b94e
    // the pending event bits that is not true.  Because accessibles may be
Packit f0b94e
    // reparented they may be the target of both a hide event and a show event
Packit f0b94e
    // at the same time.
Packit f0b94e
    if (mutEvent->GetEventType() == nsIAccessibleEvent::EVENT_SHOW) {
Packit f0b94e
      target->SetShowEventTarget(false);
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    if (mutEvent->GetEventType() == nsIAccessibleEvent::EVENT_HIDE) {
Packit f0b94e
      target->SetHideEventTarget(false);
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    // However it is not possible for a reorder event target to also be the
Packit f0b94e
    // target of a show or hide, so we can just zero that.
Packit f0b94e
    target->SetReorderEventTarget(false);
Packit f0b94e
Packit f0b94e
    mutEvent->SetPrevEvent(nullptr);
Packit f0b94e
    mutEvent->SetNextEvent(nullptr);
Packit f0b94e
    mMutationMap.RemoveEvent(mutEvent);
Packit f0b94e
    mutEvent = nextEvent;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  ProcessEventQueue();
Packit f0b94e
Packit f0b94e
  if (IPCAccessibilityActive()) {
Packit f0b94e
    size_t newDocCount = newChildDocs.Length();
Packit f0b94e
    for (size_t i = 0; i < newDocCount; i++) {
Packit f0b94e
      DocAccessible* childDoc = newChildDocs[i];
Packit f0b94e
      if (childDoc->IsDefunct()) {
Packit f0b94e
        continue;
Packit f0b94e
      }
Packit f0b94e
Packit f0b94e
      Accessible* parent = childDoc->Parent();
Packit f0b94e
      DocAccessibleChild* parentIPCDoc = mDocument->IPCDoc();
Packit f0b94e
      MOZ_DIAGNOSTIC_ASSERT(parentIPCDoc);
Packit f0b94e
      uint64_t id = reinterpret_cast<uintptr_t>(parent->UniqueID());
Packit f0b94e
      MOZ_DIAGNOSTIC_ASSERT(id);
Packit f0b94e
      DocAccessibleChild* ipcDoc = childDoc->IPCDoc();
Packit f0b94e
      if (ipcDoc) {
Packit f0b94e
        parentIPCDoc->SendBindChildDoc(ipcDoc, id);
Packit f0b94e
        continue;
Packit f0b94e
      }
Packit f0b94e
Packit f0b94e
      ipcDoc = new DocAccessibleChild(childDoc, parentIPCDoc->Manager());
Packit f0b94e
      childDoc->SetIPCDoc(ipcDoc);
Packit f0b94e
Packit f0b94e
#if defined(XP_WIN)
Packit f0b94e
      parentIPCDoc->ConstructChildDocInParentProcess(
Packit f0b94e
          ipcDoc, id, AccessibleWrap::GetChildIDFor(childDoc));
Packit f0b94e
#else
Packit f0b94e
      nsCOMPtr<nsITabChild> tabChild =
Packit f0b94e
          do_GetInterface(mDocument->DocumentNode()->GetDocShell());
Packit f0b94e
      if (tabChild) {
Packit f0b94e
        static_cast<TabChild*>(tabChild.get())
Packit f0b94e
            ->SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, id, 0, 0);
Packit f0b94e
      }
Packit f0b94e
#endif
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  mObservingState = eRefreshObserving;
Packit f0b94e
  if (!mDocument) return;
Packit f0b94e
Packit f0b94e
  // Stop further processing if there are no new notifications of any kind or
Packit f0b94e
  // events and document load is processed.
Packit f0b94e
  if (mContentInsertions.Count() == 0 && mNotifications.IsEmpty() &&
Packit f0b94e
      mEvents.IsEmpty() && mTextHash.Count() == 0 &&
Packit f0b94e
      mHangingChildDocuments.IsEmpty() &&
Packit f0b94e
      mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) &&
Packit f0b94e
      mPresShell->RemoveRefreshObserver(this, FlushType::Display)) {
Packit f0b94e
    mObservingState = eNotObservingRefresh;
Packit f0b94e
  }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void NotificationController::EventMap::PutEvent(AccTreeMutationEvent* aEvent) {
Packit f0b94e
  EventType type = GetEventType(aEvent);
Packit f0b94e
  uint64_t addr = reinterpret_cast<uintptr_t>(aEvent->GetAccessible());
Packit f0b94e
  MOZ_ASSERT((addr & 0x3) == 0, "accessible is not 4 byte aligned");
Packit f0b94e
  addr |= type;
Packit f0b94e
  mTable.Put(addr, aEvent);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
AccTreeMutationEvent* NotificationController::EventMap::GetEvent(
Packit f0b94e
    Accessible* aTarget, EventType aType) {
Packit f0b94e
  uint64_t addr = reinterpret_cast<uintptr_t>(aTarget);
Packit f0b94e
  MOZ_ASSERT((addr & 0x3) == 0, "target is not 4 byte aligned");
Packit f0b94e
Packit f0b94e
  addr |= aType;
Packit f0b94e
  return mTable.GetWeak(addr);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void NotificationController::EventMap::RemoveEvent(
Packit f0b94e
    AccTreeMutationEvent* aEvent) {
Packit f0b94e
  EventType type = GetEventType(aEvent);
Packit f0b94e
  uint64_t addr = reinterpret_cast<uintptr_t>(aEvent->GetAccessible());
Packit f0b94e
  MOZ_ASSERT((addr & 0x3) == 0, "accessible is not 4 byte aligned");
Packit f0b94e
  addr |= type;
Packit f0b94e
Packit f0b94e
  MOZ_ASSERT(mTable.GetWeak(addr) == aEvent, "mTable has the wrong event");
Packit f0b94e
  mTable.Remove(addr);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
NotificationController::EventMap::EventType
Packit f0b94e
NotificationController::EventMap::GetEventType(AccTreeMutationEvent* aEvent) {
Packit f0b94e
  switch (aEvent->GetEventType()) {
Packit f0b94e
    case nsIAccessibleEvent::EVENT_SHOW:
Packit f0b94e
      return ShowEvent;
Packit f0b94e
    case nsIAccessibleEvent::EVENT_HIDE:
Packit f0b94e
      return HideEvent;
Packit f0b94e
    case nsIAccessibleEvent::EVENT_REORDER:
Packit f0b94e
      return ReorderEvent;
Packit f0b94e
    default:
Packit f0b94e
      MOZ_ASSERT_UNREACHABLE("event has invalid type");
Packit f0b94e
      return ShowEvent;
Packit f0b94e
  }
Packit f0b94e
}