LCOV - code coverage report
Current view: top level - accessible/base - NotificationController.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 501 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 26 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "NotificationController.h"
       7             : 
       8             : #include "DocAccessible-inl.h"
       9             : #include "DocAccessibleChild.h"
      10             : #include "TextLeafAccessible.h"
      11             : #include "TextUpdater.h"
      12             : 
      13             : #include "mozilla/dom/TabChild.h"
      14             : #include "mozilla/dom/Element.h"
      15             : #include "mozilla/Telemetry.h"
      16             : 
      17             : using namespace mozilla;
      18             : using namespace mozilla::a11y;
      19             : 
      20             : ////////////////////////////////////////////////////////////////////////////////
      21             : // NotificationCollector
      22             : ////////////////////////////////////////////////////////////////////////////////
      23             : 
      24           0 : NotificationController::NotificationController(DocAccessible* aDocument,
      25           0 :                                                nsIPresShell* aPresShell) :
      26             :   EventQueue(aDocument), mObservingState(eNotObservingRefresh),
      27           0 :   mPresShell(aPresShell), mEventGeneration(0)
      28             : {
      29             : #ifdef DEBUG
      30           0 :   mMoveGuardOnStack = false;
      31             : #endif
      32             : 
      33             :   // Schedule initial accessible tree construction.
      34           0 :   ScheduleProcessing();
      35           0 : }
      36             : 
      37           0 : NotificationController::~NotificationController()
      38             : {
      39           0 :   NS_ASSERTION(!mDocument, "Controller wasn't shutdown properly!");
      40           0 :   if (mDocument)
      41           0 :     Shutdown();
      42           0 : }
      43             : 
      44             : ////////////////////////////////////////////////////////////////////////////////
      45             : // NotificationCollector: AddRef/Release and cycle collection
      46             : 
      47           0 : NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(NotificationController)
      48           0 : NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE(NotificationController)
      49             : 
      50             : NS_IMPL_CYCLE_COLLECTION_CLASS(NotificationController)
      51             : 
      52           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NotificationController)
      53           0 :   if (tmp->mDocument)
      54           0 :     tmp->Shutdown();
      55           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
      56             : 
      57           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NotificationController)
      58           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHangingChildDocuments)
      59           0 :   for (auto it = tmp->mContentInsertions.ConstIter(); !it.Done(); it.Next()) {
      60           0 :     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mContentInsertions key");
      61           0 :     cb.NoteXPCOMChild(it.Key());
      62           0 :     nsTArray<nsCOMPtr<nsIContent>>* list = it.UserData();
      63           0 :     for (uint32_t i = 0; i < list->Length(); i++) {
      64             :       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
      65           0 :                                          "mContentInsertions value item");
      66           0 :       cb.NoteXPCOMChild(list->ElementAt(i));
      67             :     }
      68             :   }
      69           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvents)
      70           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRelocations)
      71           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
      72             : 
      73           0 : NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(NotificationController, AddRef)
      74           0 : NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(NotificationController, Release)
      75             : 
      76             : ////////////////////////////////////////////////////////////////////////////////
      77             : // NotificationCollector: public
      78             : 
      79             : void
      80           0 : NotificationController::Shutdown()
      81             : {
      82           0 :   if (mObservingState != eNotObservingRefresh &&
      83           0 :       mPresShell->RemoveRefreshObserver(this, FlushType::Display)) {
      84           0 :     mObservingState = eNotObservingRefresh;
      85             :   }
      86             : 
      87             :   // Shutdown handling child documents.
      88           0 :   int32_t childDocCount = mHangingChildDocuments.Length();
      89           0 :   for (int32_t idx = childDocCount - 1; idx >= 0; idx--) {
      90           0 :     if (!mHangingChildDocuments[idx]->IsDefunct())
      91           0 :       mHangingChildDocuments[idx]->Shutdown();
      92             :   }
      93             : 
      94           0 :   mHangingChildDocuments.Clear();
      95             : 
      96           0 :   mDocument = nullptr;
      97           0 :   mPresShell = nullptr;
      98             : 
      99           0 :   mTextHash.Clear();
     100           0 :   mContentInsertions.Clear();
     101           0 :   mNotifications.Clear();
     102           0 :   mEvents.Clear();
     103           0 :   mRelocations.Clear();
     104           0 :   mEventTree.Clear();
     105           0 : }
     106             : 
     107             : EventTree*
     108           0 : NotificationController::QueueMutation(Accessible* aContainer)
     109             : {
     110           0 :   EventTree* tree = mEventTree.FindOrInsert(aContainer);
     111           0 :   if (tree) {
     112           0 :     ScheduleProcessing();
     113             :   }
     114           0 :   return tree;
     115             : }
     116             : 
     117             : bool
     118           0 : NotificationController::QueueMutationEvent(AccTreeMutationEvent* aEvent)
     119             : {
     120             :   // We have to allow there to be a hide and then a show event for a target
     121             :   // because of targets getting moved.  However we need to coalesce a show and
     122             :   // then a hide for a target which means we need to check for that here.
     123           0 :   if (aEvent->GetEventType() == nsIAccessibleEvent::EVENT_HIDE  &&
     124           0 :       aEvent->GetAccessible()->ShowEventTarget()) {
     125           0 :     AccTreeMutationEvent* showEvent = mMutationMap.GetEvent(aEvent->GetAccessible(), EventMap::ShowEvent);
     126           0 :     DropMutationEvent(showEvent);
     127           0 :     return false;
     128             :   }
     129             : 
     130           0 :   AccMutationEvent* mutEvent = downcast_accEvent(aEvent);
     131           0 :   mEventGeneration++;
     132           0 :   mutEvent->SetEventGeneration(mEventGeneration);
     133             : 
     134           0 :   if (!mFirstMutationEvent) {
     135           0 :     mFirstMutationEvent = aEvent;
     136           0 :     ScheduleProcessing();
     137             :   }
     138             : 
     139           0 :   if (mLastMutationEvent) {
     140           0 :     NS_ASSERTION(!mLastMutationEvent->NextEvent(), "why isn't the last event the end?");
     141           0 :     mLastMutationEvent->SetNextEvent(aEvent);
     142             :   }
     143             : 
     144           0 :   aEvent->SetPrevEvent(mLastMutationEvent);
     145           0 :   mLastMutationEvent = aEvent;
     146           0 :   mMutationMap.PutEvent(aEvent);
     147             : 
     148             :   // Because we could be hiding the target of a show event we need to get rid
     149             :   // of any such events.  It may be possible to do less than coallesce all
     150             :   // events, however that is easiest.
     151           0 :   if (aEvent->GetEventType() == nsIAccessibleEvent::EVENT_HIDE) {
     152           0 :     CoalesceMutationEvents();
     153             : 
     154             :     // mLastMutationEvent will point to something other than aEvent if and only
     155             :     // if aEvent was just coalesced away.  In that case a parent accessible
     156             :     // must already have the required reorder and text change events so we are
     157             :     // done here.
     158           0 :     if (mLastMutationEvent != aEvent) {
     159           0 :       return false;
     160             :     }
     161             :   }
     162             : 
     163             :   // We need to fire a reorder event after all of the events targeted at shown or
     164             :   // hidden children of a container.  So either queue a new one, or move an
     165             :   // existing one to the end of the queue if the container already has a
     166             :   // reorder event.
     167           0 :   Accessible* target = aEvent->GetAccessible();
     168           0 :   Accessible* container = aEvent->GetAccessible()->Parent();
     169           0 :   RefPtr<AccReorderEvent> reorder;
     170           0 :   if (!container->ReorderEventTarget()) {
     171           0 :     reorder = new AccReorderEvent(container);
     172           0 :     container->SetReorderEventTarget(true);
     173           0 :     mMutationMap.PutEvent(reorder);
     174             : 
     175             :     // Since this is the first child of container that is changing, the name of
     176             :     // container may be changing.
     177           0 :     QueueNameChange(target);
     178             :   } else {
     179           0 :     AccReorderEvent* event = downcast_accEvent(mMutationMap.GetEvent(container, EventMap::ReorderEvent));
     180           0 :     reorder = event;
     181           0 :     if (mFirstMutationEvent == event) {
     182           0 :       mFirstMutationEvent = event->NextEvent();
     183             :     } else {
     184           0 :       event->PrevEvent()->SetNextEvent(event->NextEvent());
     185             :     }
     186             : 
     187           0 :       event->NextEvent()->SetPrevEvent(event->PrevEvent());
     188           0 :       event->SetNextEvent(nullptr);
     189             :   }
     190             : 
     191           0 :   reorder->SetEventGeneration(mEventGeneration);
     192           0 :   reorder->SetPrevEvent(mLastMutationEvent);
     193           0 :   mLastMutationEvent->SetNextEvent(reorder);
     194           0 :   mLastMutationEvent = reorder;
     195             : 
     196             :   // It is not possible to have a text change event for something other than a
     197             :   // hyper text accessible.
     198           0 :   if (!container->IsHyperText()) {
     199           0 :     return true;
     200             :   }
     201             : 
     202           0 :   MOZ_ASSERT(mutEvent);
     203             : 
     204           0 :   nsString text;
     205           0 :   aEvent->GetAccessible()->AppendTextTo(text);
     206           0 :   if (text.IsEmpty()) {
     207           0 :     return true;
     208             :   }
     209             : 
     210           0 :   int32_t offset = container->AsHyperText()->GetChildOffset(target);
     211           0 :   AccTreeMutationEvent* prevEvent = aEvent->PrevEvent();
     212           0 :   while (prevEvent && prevEvent->GetEventType() == nsIAccessibleEvent::EVENT_REORDER) {
     213           0 :     prevEvent = prevEvent->PrevEvent();
     214             :   }
     215             : 
     216           0 :   if (prevEvent && prevEvent->GetEventType() == nsIAccessibleEvent::EVENT_HIDE &&
     217           0 :       mutEvent->IsHide()) {
     218           0 :     AccHideEvent* prevHide = downcast_accEvent(prevEvent);
     219           0 :     AccTextChangeEvent* prevTextChange = prevHide->mTextChangeEvent;
     220           0 :     if (prevTextChange && prevHide->Parent() == mutEvent->Parent()) {
     221           0 :       if (prevHide->mNextSibling == target) {
     222           0 :         target->AppendTextTo(prevTextChange->mModifiedText);
     223           0 :         prevHide->mTextChangeEvent.swap(mutEvent->mTextChangeEvent);
     224           0 :       } else if (prevHide->mPrevSibling == target) {
     225           0 :         nsString temp;
     226           0 :         target->AppendTextTo(temp);
     227             : 
     228           0 :         uint32_t extraLen = temp.Length();
     229           0 :         temp += prevTextChange->mModifiedText;;
     230           0 :         prevTextChange->mModifiedText = temp;
     231           0 :         prevTextChange->mStart -= extraLen;
     232           0 :         prevHide->mTextChangeEvent.swap(mutEvent->mTextChangeEvent);
     233             :       }
     234             :     }
     235           0 :   } else if (prevEvent && mutEvent->IsShow() &&
     236           0 :              prevEvent->GetEventType() == nsIAccessibleEvent::EVENT_SHOW) {
     237           0 :     AccShowEvent* prevShow = downcast_accEvent(prevEvent);
     238           0 :     AccTextChangeEvent* prevTextChange = prevShow->mTextChangeEvent;
     239           0 :     if (prevTextChange && prevShow->Parent() == target->Parent()) {
     240           0 :       int32_t index = target->IndexInParent();
     241           0 :       int32_t prevIndex = prevShow->GetAccessible()->IndexInParent();
     242           0 :       if (prevIndex + 1 == index) {
     243           0 :         target->AppendTextTo(prevTextChange->mModifiedText);
     244           0 :         prevShow->mTextChangeEvent.swap(mutEvent->mTextChangeEvent);
     245           0 :       } else if (index + 1 == prevIndex) {
     246           0 :         nsString temp;
     247           0 :         target->AppendTextTo(temp);
     248           0 :         prevTextChange->mStart -= temp.Length();
     249           0 :         temp += prevTextChange->mModifiedText;
     250           0 :         prevTextChange->mModifiedText = temp;
     251           0 :         prevShow->mTextChangeEvent.swap(mutEvent->mTextChangeEvent);
     252             :       }
     253             :     }
     254             :   }
     255             : 
     256           0 :   if (!mutEvent->mTextChangeEvent) {
     257             :     mutEvent->mTextChangeEvent =
     258           0 :       new AccTextChangeEvent(container, offset, text, mutEvent->IsShow(),
     259           0 :                              aEvent->mIsFromUserInput ? eFromUserInput : eNoUserInput);
     260             :   }
     261             : 
     262           0 :   return true;
     263             : }
     264             : 
     265             : void
     266           0 : NotificationController::DropMutationEvent(AccTreeMutationEvent* aEvent)
     267             : {
     268             :   // unset the event bits since the event isn't being fired any more.
     269           0 :   if (aEvent->GetEventType() == nsIAccessibleEvent::EVENT_REORDER) {
     270           0 :     aEvent->GetAccessible()->SetReorderEventTarget(false);
     271           0 :   } else if (aEvent->GetEventType() == nsIAccessibleEvent::EVENT_SHOW) {
     272           0 :     aEvent->GetAccessible()->SetShowEventTarget(false);
     273             :   } else {
     274           0 :     AccHideEvent* hideEvent = downcast_accEvent(aEvent);
     275           0 :     MOZ_ASSERT(hideEvent);
     276             : 
     277           0 :     if (hideEvent->NeedsShutdown()) {
     278           0 :       mDocument->ShutdownChildrenInSubtree(aEvent->GetAccessible());
     279             :     }
     280             :   }
     281             : 
     282             :   // Do the work to splice the event out of the list.
     283           0 :   if (mFirstMutationEvent == aEvent) {
     284           0 :     mFirstMutationEvent = aEvent->NextEvent();
     285             :   } else {
     286           0 :     aEvent->PrevEvent()->SetNextEvent(aEvent->NextEvent());
     287             :   }
     288             : 
     289           0 :   if (mLastMutationEvent == aEvent) {
     290           0 :     mLastMutationEvent = aEvent->PrevEvent();
     291             :   } else {
     292           0 :     aEvent->NextEvent()->SetPrevEvent(aEvent->PrevEvent());
     293             :   }
     294             : 
     295           0 :   aEvent->SetPrevEvent(nullptr);
     296           0 :   aEvent->SetNextEvent(nullptr);
     297           0 :   mMutationMap.RemoveEvent(aEvent);
     298           0 : }
     299             : 
     300             : void
     301           0 : NotificationController::CoalesceMutationEvents()
     302             : {
     303           0 :   AccTreeMutationEvent* event = mFirstMutationEvent;
     304           0 :   while (event) {
     305           0 :     AccTreeMutationEvent* nextEvent = event->NextEvent();
     306           0 :     uint32_t eventType = event->GetEventType();
     307           0 :     if (event->GetEventType() == nsIAccessibleEvent::EVENT_REORDER) {
     308           0 :       Accessible* acc = event->GetAccessible();
     309           0 :       while (acc) {
     310           0 :         if (acc->IsDoc()) {
     311           0 :           break;
     312             :         }
     313             : 
     314             :         // if a parent of the reorder event's target is being hidden that
     315             :         // hide event's target must have a parent that is also a reorder event
     316             :         // target.  That means we don't need this reorder event.
     317           0 :         if (acc->HideEventTarget()) {
     318           0 :           DropMutationEvent(event);
     319           0 :           break;
     320             :         }
     321             : 
     322           0 :         Accessible* parent = acc->Parent();
     323           0 :         if (parent->ReorderEventTarget()) {
     324           0 :           AccReorderEvent* reorder = downcast_accEvent(mMutationMap.GetEvent(parent, EventMap::ReorderEvent));
     325             : 
     326             :           // We want to make sure that a reorder event comes after any show or
     327             :           // hide events targeted at the children of its target.  We keep the
     328             :           // invariant that event generation goes up as you are farther in the
     329             :           // queue, so we want to use the spot of the event with the higher
     330             :           // generation number, and keep that generation number.
     331           0 :           if (reorder && reorder->EventGeneration() < event->EventGeneration()) {
     332           0 :             reorder->SetEventGeneration(event->EventGeneration());
     333             : 
     334             :             // It may be true that reorder was before event, and we coalesced
     335             :             // away all the show / hide events between them.  In that case
     336             :             // event is already immediately after reorder in the queue and we
     337             :             // do not need to rearrange the list of events.
     338           0 :             if (event != reorder->NextEvent()) {
     339             :               // There really should be a show or hide event before the first
     340             :               // reorder event.
     341           0 :               if (reorder->PrevEvent()) {
     342           0 :                 reorder->PrevEvent()->SetNextEvent(reorder->NextEvent());
     343             :               } else {
     344           0 :                 mFirstMutationEvent = reorder->NextEvent();
     345             :               }
     346             : 
     347           0 :               reorder->NextEvent()->SetPrevEvent(reorder->PrevEvent());
     348           0 :               event->PrevEvent()->SetNextEvent(reorder);
     349           0 :               reorder->SetPrevEvent(event->PrevEvent());
     350           0 :               event->SetPrevEvent(reorder);
     351           0 :               reorder->SetNextEvent(event);
     352             :             }
     353             :           }
     354           0 :           DropMutationEvent(event);
     355           0 :           break;
     356             :         }
     357             : 
     358           0 :         acc = parent;
     359             :       }
     360           0 :     } else if (eventType == nsIAccessibleEvent::EVENT_SHOW) {
     361           0 :       Accessible* parent = event->GetAccessible()->Parent();
     362           0 :       while (parent) {
     363           0 :         if (parent->IsDoc()) {
     364           0 :           break;
     365             :         }
     366             : 
     367             :         // if the parent of a show event is being either shown or hidden then
     368             :         // we don't need to fire a show event for a subtree of that change.
     369           0 :         if (parent->ShowEventTarget() || parent->HideEventTarget()) {
     370           0 :           DropMutationEvent(event);
     371           0 :           break;
     372             :         }
     373             : 
     374           0 :         parent = parent->Parent();
     375             :       }
     376             :     } else {
     377           0 :       MOZ_ASSERT(eventType == nsIAccessibleEvent::EVENT_HIDE, "mutation event list has an invalid event");
     378             : 
     379           0 :       AccHideEvent* hideEvent = downcast_accEvent(event);
     380           0 :       Accessible* parent = hideEvent->Parent();
     381           0 :       while (parent) {
     382           0 :         if (parent->IsDoc()) {
     383           0 :           break;
     384             :         }
     385             : 
     386           0 :         if (parent->HideEventTarget()) {
     387           0 :           DropMutationEvent(event);
     388           0 :           break;
     389             :         }
     390             : 
     391           0 :         if (parent->ShowEventTarget()) {
     392           0 :           AccShowEvent* showEvent = downcast_accEvent(mMutationMap.GetEvent(parent, EventMap::ShowEvent));
     393           0 :           if (showEvent->EventGeneration() < hideEvent->EventGeneration()) {
     394           0 :             DropMutationEvent(hideEvent);
     395           0 :             break;
     396             :           }
     397             :         }
     398             : 
     399           0 :         parent = parent->Parent();
     400             :       }
     401             :     }
     402             : 
     403           0 :     event = nextEvent;
     404             :   }
     405           0 : }
     406             : 
     407             : void
     408           0 : NotificationController::ScheduleChildDocBinding(DocAccessible* aDocument)
     409             : {
     410             :   // Schedule child document binding to the tree.
     411           0 :   mHangingChildDocuments.AppendElement(aDocument);
     412           0 :   ScheduleProcessing();
     413           0 : }
     414             : 
     415             : void
     416           0 : NotificationController::ScheduleContentInsertion(Accessible* aContainer,
     417             :                                                  nsIContent* aStartChildNode,
     418             :                                                  nsIContent* aEndChildNode)
     419             : {
     420             :   nsTArray<nsCOMPtr<nsIContent>>* list =
     421           0 :     mContentInsertions.LookupOrAdd(aContainer);
     422             : 
     423           0 :   bool needsProcessing = false;
     424           0 :   nsIContent* node = aStartChildNode;
     425           0 :   while (node != aEndChildNode) {
     426             :     // Notification triggers for content insertion even if no content was
     427             :     // actually inserted, check if the given content has a frame to discard
     428             :     // this case early.
     429           0 :     if (node->GetPrimaryFrame()) {
     430           0 :       if (list->AppendElement(node))
     431           0 :         needsProcessing = true;
     432             :     }
     433           0 :     node = node->GetNextSibling();
     434             :   }
     435             : 
     436           0 :   if (needsProcessing) {
     437           0 :     ScheduleProcessing();
     438             :   }
     439           0 : }
     440             : 
     441             : void
     442           0 : NotificationController::ScheduleProcessing()
     443             : {
     444             :   // If notification flush isn't planed yet start notification flush
     445             :   // asynchronously (after style and layout).
     446           0 :   if (mObservingState == eNotObservingRefresh) {
     447           0 :     if (mPresShell->AddRefreshObserver(this, FlushType::Display))
     448           0 :       mObservingState = eRefreshObserving;
     449             :   }
     450           0 : }
     451             : 
     452             : ////////////////////////////////////////////////////////////////////////////////
     453             : // NotificationCollector: protected
     454             : 
     455             : bool
     456           0 : NotificationController::IsUpdatePending()
     457             : {
     458           0 :   return mPresShell->IsLayoutFlushObserver() ||
     459           0 :     mObservingState == eRefreshProcessingForUpdate ||
     460           0 :     mContentInsertions.Count() != 0 || mNotifications.Length() != 0 ||
     461           0 :     mTextHash.Count() != 0 ||
     462           0 :     !mDocument->HasLoadState(DocAccessible::eTreeConstructed);
     463             : }
     464             : 
     465             : void
     466           0 : NotificationController::ProcessMutationEvents()
     467             : {
     468             :   // there is no reason to fire a hide event for a child of a show event
     469             :   // target.  That can happen if something is inserted into the tree and
     470             :   // removed before the next refresh driver tick, but it should not be
     471             :   // observable outside gecko so it should be safe to coalesce away any such
     472             :   // events.  This means that it should be fine to fire all of the hide events
     473             :   // first, and then deal with any shown subtrees.
     474           0 :   for (AccTreeMutationEvent* event = mFirstMutationEvent;
     475           0 :        event; event = event->NextEvent()) {
     476           0 :     if (event->GetEventType() != nsIAccessibleEvent::EVENT_HIDE) {
     477           0 :       continue;
     478             :     }
     479             : 
     480           0 :     nsEventShell::FireEvent(event);
     481           0 :     if (!mDocument) {
     482           0 :       return;
     483             :     }
     484             : 
     485           0 :     AccMutationEvent* mutEvent = downcast_accEvent(event);
     486           0 :     if (mutEvent->mTextChangeEvent) {
     487           0 :       nsEventShell::FireEvent(mutEvent->mTextChangeEvent);
     488           0 :       if (!mDocument) {
     489           0 :         return;
     490             :       }
     491             :     }
     492             : 
     493             :     // Fire menupopup end event before a hide event if a menu goes away.
     494             : 
     495             :     // XXX: We don't look into children of hidden subtree to find hiding
     496             :     // menupopup (as we did prior bug 570275) because we don't do that when
     497             :     // menu is showing (and that's impossible until bug 606924 is fixed).
     498             :     // Nevertheless we should do this at least because layout coalesces
     499             :     // the changes before our processing and we may miss some menupopup
     500             :     // events. Now we just want to be consistent in content insertion/removal
     501             :     // handling.
     502           0 :     if (event->mAccessible->ARIARole() == roles::MENUPOPUP) {
     503           0 :       nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
     504           0 :                               event->mAccessible);
     505           0 :       if (!mDocument) {
     506           0 :         return;
     507             :       }
     508             :     }
     509             : 
     510           0 :     AccHideEvent* hideEvent = downcast_accEvent(event);
     511           0 :     if (hideEvent->NeedsShutdown()) {
     512           0 :       mDocument->ShutdownChildrenInSubtree(event->mAccessible);
     513             :     }
     514             :   }
     515             : 
     516             :   // Group the show events by the parent of their target.
     517           0 :   nsDataHashtable<nsPtrHashKey<Accessible>, nsTArray<AccTreeMutationEvent*>> showEvents;
     518           0 :   for (AccTreeMutationEvent* event = mFirstMutationEvent;
     519           0 :        event; event = event->NextEvent()) {
     520           0 :     if (event->GetEventType() != nsIAccessibleEvent::EVENT_SHOW) {
     521           0 :       continue;
     522             :     }
     523             : 
     524           0 :     Accessible* parent = event->GetAccessible()->Parent();
     525           0 :     showEvents.GetOrInsert(parent).AppendElement(event);
     526             :   }
     527             : 
     528             :   // We need to fire show events for the children of an accessible in the order
     529             :   // of their indices at this point.  So sort each set of events for the same
     530             :   // container by the index of their target.
     531           0 :   for (auto iter = showEvents.Iter(); !iter.Done(); iter.Next()) {
     532             :     struct AccIdxComparator {
     533           0 :       bool LessThan(const AccTreeMutationEvent* a, const AccTreeMutationEvent* b) const
     534             :       {
     535           0 :         int32_t aIdx = a->GetAccessible()->IndexInParent();
     536           0 :         int32_t bIdx = b->GetAccessible()->IndexInParent();
     537           0 :         MOZ_ASSERT(aIdx >= 0 && bIdx >= 0 && aIdx != bIdx);
     538           0 :         return aIdx < bIdx;
     539             :       }
     540           0 :       bool Equals(const AccTreeMutationEvent* a, const AccTreeMutationEvent* b) const
     541             :       {
     542           0 :         DebugOnly<int32_t> aIdx = a->GetAccessible()->IndexInParent();
     543           0 :         DebugOnly<int32_t> bIdx = b->GetAccessible()->IndexInParent();
     544           0 :         MOZ_ASSERT(aIdx >= 0 && bIdx >= 0 && aIdx != bIdx);
     545           0 :         return false;
     546             :       }
     547             :     };
     548             : 
     549           0 :     nsTArray<AccTreeMutationEvent*>& events = iter.Data();
     550           0 :     events.Sort(AccIdxComparator());
     551           0 :     for (AccTreeMutationEvent* event: events) {
     552           0 :       nsEventShell::FireEvent(event);
     553           0 :       if (!mDocument) {
     554           0 :         return;
     555             :       }
     556             : 
     557           0 :       AccMutationEvent* mutEvent = downcast_accEvent(event);
     558           0 :       if (mutEvent->mTextChangeEvent) {
     559           0 :         nsEventShell::FireEvent(mutEvent->mTextChangeEvent);
     560           0 :         if (!mDocument) {
     561           0 :           return;
     562             :         }
     563             :       }
     564             :     }
     565             :   }
     566             : 
     567             :   // Now we can fire the reorder events after all the show and hide events.
     568           0 :   for (AccTreeMutationEvent* event = mFirstMutationEvent;
     569           0 :        event; event = event->NextEvent()) {
     570           0 :     if (event->GetEventType() != nsIAccessibleEvent::EVENT_REORDER) {
     571           0 :       continue;
     572             :     }
     573             : 
     574           0 :     nsEventShell::FireEvent(event);
     575           0 :     if (!mDocument) {
     576           0 :       return;
     577             :     }
     578             : 
     579           0 :     Accessible* target = event->GetAccessible();
     580           0 :     target->Document()->MaybeNotifyOfValueChange(target);
     581           0 :     if (!mDocument) {
     582           0 :       return;
     583             :     }
     584             :   }
     585             : }
     586             : 
     587             : ////////////////////////////////////////////////////////////////////////////////
     588             : // NotificationCollector: private
     589             : 
     590             : void
     591           0 : NotificationController::WillRefresh(mozilla::TimeStamp aTime)
     592             : {
     593           0 :   AUTO_PROFILER_LABEL("NotificationController::WillRefresh", OTHER);
     594             : 
     595             :   // If the document accessible that notification collector was created for is
     596             :   // now shut down, don't process notifications anymore.
     597           0 :   NS_ASSERTION(mDocument,
     598             :                "The document was shut down while refresh observer is attached!");
     599           0 :   if (!mDocument)
     600           0 :     return;
     601             : 
     602           0 :   if (mObservingState == eRefreshProcessing ||
     603           0 :       mObservingState == eRefreshProcessingForUpdate)
     604           0 :     return;
     605             : 
     606             :   // Any generic notifications should be queued if we're processing content
     607             :   // insertions or generic notifications.
     608           0 :   mObservingState = eRefreshProcessingForUpdate;
     609             : 
     610             :   // Initial accessible tree construction.
     611           0 :   if (!mDocument->HasLoadState(DocAccessible::eTreeConstructed)) {
     612             :     // If document is not bound to parent at this point then the document is not
     613             :     // ready yet (process notifications later).
     614           0 :     if (!mDocument->IsBoundToParent()) {
     615           0 :       mObservingState = eRefreshObserving;
     616           0 :       return;
     617             :     }
     618             : 
     619             : #ifdef A11Y_LOG
     620           0 :     if (logging::IsEnabled(logging::eTree)) {
     621           0 :       logging::MsgBegin("TREE", "initial tree created");
     622           0 :       logging::Address("document", mDocument);
     623           0 :       logging::MsgEnd();
     624             :     }
     625             : #endif
     626             : 
     627           0 :     mDocument->DoInitialUpdate();
     628             : 
     629           0 :     NS_ASSERTION(mContentInsertions.Count() == 0,
     630             :                  "Pending content insertions while initial accessible tree isn't created!");
     631             :   }
     632             : 
     633             :   // Initialize scroll support if needed.
     634           0 :   if (!(mDocument->mDocFlags & DocAccessible::eScrollInitialized))
     635           0 :     mDocument->AddScrollListener();
     636             : 
     637             :   // Process rendered text change notifications.
     638           0 :   for (auto iter = mTextHash.Iter(); !iter.Done(); iter.Next()) {
     639           0 :     nsCOMPtrHashKey<nsIContent>* entry = iter.Get();
     640           0 :     nsIContent* textNode = entry->GetKey();
     641           0 :     Accessible* textAcc = mDocument->GetAccessible(textNode);
     642             : 
     643             :     // If the text node is not in tree or doesn't have frame then this case should
     644             :     // have been handled already by content removal notifications.
     645           0 :     nsINode* containerNode = textNode->GetParentNode();
     646           0 :     if (!containerNode) {
     647           0 :       NS_ASSERTION(!textAcc,
     648             :                    "Text node was removed but accessible is kept alive!");
     649           0 :       continue;
     650             :     }
     651             : 
     652           0 :     nsIFrame* textFrame = textNode->GetPrimaryFrame();
     653           0 :     if (!textFrame) {
     654           0 :       NS_ASSERTION(!textAcc,
     655             :                    "Text node isn't rendered but accessible is kept alive!");
     656           0 :       continue;
     657             :     }
     658             : 
     659             :   #ifdef A11Y_LOG
     660           0 :     nsIContent* containerElm = containerNode->IsElement() ?
     661           0 :       containerNode->AsElement() : nullptr;
     662             :   #endif
     663             : 
     664             :     nsIFrame::RenderedText text = textFrame->GetRenderedText(0,
     665             :         UINT32_MAX, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
     666           0 :         nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
     667             : 
     668             :     // Remove text accessible if rendered text is empty.
     669           0 :     if (textAcc) {
     670           0 :       if (text.mString.IsEmpty()) {
     671             :   #ifdef A11Y_LOG
     672           0 :         if (logging::IsEnabled(logging::eTree | logging::eText)) {
     673           0 :           logging::MsgBegin("TREE", "text node lost its content; doc: %p", mDocument);
     674           0 :           logging::Node("container", containerElm);
     675           0 :           logging::Node("content", textNode);
     676           0 :           logging::MsgEnd();
     677             :         }
     678             :   #endif
     679             : 
     680           0 :         mDocument->ContentRemoved(textAcc);
     681           0 :         continue;
     682             :       }
     683             : 
     684             :       // Update text of the accessible and fire text change events.
     685             :   #ifdef A11Y_LOG
     686           0 :       if (logging::IsEnabled(logging::eText)) {
     687           0 :         logging::MsgBegin("TEXT", "text may be changed; doc: %p", mDocument);
     688           0 :         logging::Node("container", containerElm);
     689           0 :         logging::Node("content", textNode);
     690           0 :         logging::MsgEntry("old text '%s'",
     691           0 :                           NS_ConvertUTF16toUTF8(textAcc->AsTextLeaf()->Text()).get());
     692           0 :         logging::MsgEntry("new text: '%s'",
     693           0 :                           NS_ConvertUTF16toUTF8(text.mString).get());
     694           0 :         logging::MsgEnd();
     695             :       }
     696             :   #endif
     697             : 
     698           0 :       TextUpdater::Run(mDocument, textAcc->AsTextLeaf(), text.mString);
     699           0 :       continue;
     700             :     }
     701             : 
     702             :     // Append an accessible if rendered text is not empty.
     703           0 :     if (!text.mString.IsEmpty()) {
     704             :   #ifdef A11Y_LOG
     705           0 :       if (logging::IsEnabled(logging::eTree | logging::eText)) {
     706           0 :         logging::MsgBegin("TREE", "text node gains new content; doc: %p", mDocument);
     707           0 :         logging::Node("container", containerElm);
     708           0 :         logging::Node("content", textNode);
     709           0 :         logging::MsgEnd();
     710             :       }
     711             :   #endif
     712             : 
     713           0 :       Accessible* container = mDocument->AccessibleOrTrueContainer(containerNode);
     714           0 :       MOZ_ASSERT(container,
     715             :                  "Text node having rendered text hasn't accessible document!");
     716           0 :       if (container) {
     717             :         nsTArray<nsCOMPtr<nsIContent>>* list =
     718           0 :           mContentInsertions.LookupOrAdd(container);
     719           0 :         list->AppendElement(textNode);
     720             :       }
     721             :     }
     722             :   }
     723           0 :   mTextHash.Clear();
     724             : 
     725             :   // Process content inserted notifications to update the tree.
     726           0 :   for (auto iter = mContentInsertions.ConstIter(); !iter.Done(); iter.Next()) {
     727           0 :     mDocument->ProcessContentInserted(iter.Key(), iter.UserData());
     728           0 :     if (!mDocument) {
     729           0 :       return;
     730             :     }
     731             :   }
     732           0 :   mContentInsertions.Clear();
     733             : 
     734             :   // Bind hanging child documents unless we are using IPC and the
     735             :   // document has no IPC actor.  If we fail to bind the child doc then
     736             :   // shut it down.
     737           0 :   uint32_t hangingDocCnt = mHangingChildDocuments.Length();
     738           0 :   nsTArray<RefPtr<DocAccessible>> newChildDocs;
     739           0 :   for (uint32_t idx = 0; idx < hangingDocCnt; idx++) {
     740           0 :     DocAccessible* childDoc = mHangingChildDocuments[idx];
     741           0 :     if (childDoc->IsDefunct())
     742           0 :       continue;
     743             : 
     744           0 :     if (IPCAccessibilityActive() && !mDocument->IPCDoc()) {
     745           0 :       childDoc->Shutdown();
     746           0 :       continue;
     747             :     }
     748             : 
     749           0 :     nsIContent* ownerContent = mDocument->DocumentNode()->
     750           0 :       FindContentForSubDocument(childDoc->DocumentNode());
     751           0 :     if (ownerContent) {
     752           0 :       Accessible* outerDocAcc = mDocument->GetAccessible(ownerContent);
     753           0 :       if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) {
     754           0 :         if (mDocument->AppendChildDocument(childDoc)) {
     755           0 :           newChildDocs.AppendElement(Move(mHangingChildDocuments[idx]));
     756           0 :           continue;
     757             :         }
     758             : 
     759           0 :         outerDocAcc->RemoveChild(childDoc);
     760             :       }
     761             : 
     762             :       // Failed to bind the child document, destroy it.
     763           0 :       childDoc->Shutdown();
     764             :     }
     765             :   }
     766             : 
     767             :   // Clear the hanging documents list, even if we didn't bind them.
     768           0 :   mHangingChildDocuments.Clear();
     769           0 :   MOZ_ASSERT(mDocument, "Illicit document shutdown");
     770           0 :   if (!mDocument) {
     771           0 :     return;
     772             :   }
     773             : 
     774             :   // If the document is ready and all its subdocuments are completely loaded
     775             :   // then process the document load.
     776           0 :   if (mDocument->HasLoadState(DocAccessible::eReady) &&
     777           0 :       !mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) &&
     778             :       hangingDocCnt == 0) {
     779           0 :     uint32_t childDocCnt = mDocument->ChildDocumentCount(), childDocIdx = 0;
     780           0 :     for (; childDocIdx < childDocCnt; childDocIdx++) {
     781           0 :       DocAccessible* childDoc = mDocument->GetChildDocumentAt(childDocIdx);
     782           0 :       if (!childDoc->HasLoadState(DocAccessible::eCompletelyLoaded))
     783           0 :         break;
     784             :     }
     785             : 
     786           0 :     if (childDocIdx == childDocCnt) {
     787           0 :       mDocument->ProcessLoad();
     788           0 :       if (!mDocument)
     789           0 :         return;
     790             :     }
     791             :   }
     792             : 
     793             :   // Process only currently queued generic notifications.
     794           0 :   nsTArray < RefPtr<Notification> > notifications;
     795           0 :   notifications.SwapElements(mNotifications);
     796             : 
     797           0 :   uint32_t notificationCount = notifications.Length();
     798           0 :   for (uint32_t idx = 0; idx < notificationCount; idx++) {
     799           0 :     notifications[idx]->Process();
     800           0 :     if (!mDocument)
     801           0 :       return;
     802             :   }
     803             : 
     804             :   // Process invalidation list of the document after all accessible tree
     805             :   // modification are done.
     806           0 :   mDocument->ProcessInvalidationList();
     807             : 
     808             :   // Process relocation list.
     809           0 :   for (uint32_t idx = 0; idx < mRelocations.Length(); idx++) {
     810           0 :     if (mRelocations[idx]->IsInDocument()) {
     811           0 :       mDocument->DoARIAOwnsRelocation(mRelocations[idx]);
     812             :     }
     813             :   }
     814           0 :   mRelocations.Clear();
     815             : 
     816             :   // If a generic notification occurs after this point then we may be allowed to
     817             :   // process it synchronously.  However we do not want to reenter if fireing
     818             :   // events causes script to run.
     819           0 :   mObservingState = eRefreshProcessing;
     820             : 
     821           0 :   CoalesceMutationEvents();
     822           0 :   ProcessMutationEvents();
     823           0 :   mEventGeneration = 0;
     824             : 
     825             :   // Now that we are done with them get rid of the events we fired.
     826           0 :   RefPtr<AccTreeMutationEvent> mutEvent = Move(mFirstMutationEvent);
     827           0 :   mLastMutationEvent = nullptr;
     828           0 :   mFirstMutationEvent = nullptr;
     829           0 :   while (mutEvent) {
     830           0 :     RefPtr<AccTreeMutationEvent> nextEvent = mutEvent->NextEvent();
     831           0 :     Accessible* target = mutEvent->GetAccessible();
     832             : 
     833             :     // We need to be careful here, while it may seem that we can simply 0 all
     834             :     // the pending event bits that is not true.  Because accessibles may be
     835             :     // reparented they may be the target of both a hide event and a show event
     836             :     // at the same time.
     837           0 :     if (mutEvent->GetEventType() == nsIAccessibleEvent::EVENT_SHOW) {
     838           0 :       target->SetShowEventTarget(false);
     839             :     }
     840             : 
     841           0 :     if (mutEvent->GetEventType() == nsIAccessibleEvent::EVENT_HIDE) {
     842           0 :       target->SetHideEventTarget(false);
     843             :     }
     844             : 
     845             :     // However it is not possible for a reorder event target to also be the
     846             :     // target of a show or hide, so we can just zero that.
     847           0 :     target->SetReorderEventTarget(false);
     848             : 
     849           0 :     mutEvent->SetPrevEvent(nullptr);
     850           0 :     mutEvent->SetNextEvent(nullptr);
     851           0 :     mMutationMap.RemoveEvent(mutEvent);
     852           0 :     mutEvent = nextEvent;
     853             :   }
     854             : 
     855           0 :   ProcessEventQueue();
     856             : 
     857           0 :   if (IPCAccessibilityActive()) {
     858           0 :     size_t newDocCount = newChildDocs.Length();
     859           0 :     for (size_t i = 0; i < newDocCount; i++) {
     860           0 :       DocAccessible* childDoc = newChildDocs[i];
     861           0 :       if (childDoc->IsDefunct()) {
     862           0 :         continue;
     863             :       }
     864             : 
     865           0 :       Accessible* parent = childDoc->Parent();
     866           0 :       DocAccessibleChild* parentIPCDoc = mDocument->IPCDoc();
     867           0 :       MOZ_DIAGNOSTIC_ASSERT(parentIPCDoc);
     868           0 :       uint64_t id = reinterpret_cast<uintptr_t>(parent->UniqueID());
     869           0 :       MOZ_DIAGNOSTIC_ASSERT(id);
     870           0 :       DocAccessibleChild* ipcDoc = childDoc->IPCDoc();
     871           0 :       if (ipcDoc) {
     872           0 :         parentIPCDoc->SendBindChildDoc(ipcDoc, id);
     873           0 :         continue;
     874             :       }
     875             : 
     876           0 :       ipcDoc = new DocAccessibleChild(childDoc, parentIPCDoc->Manager());
     877           0 :       childDoc->SetIPCDoc(ipcDoc);
     878             : 
     879             : #if defined(XP_WIN)
     880             :       parentIPCDoc->ConstructChildDocInParentProcess(ipcDoc, id,
     881             :                                                      AccessibleWrap::GetChildIDFor(childDoc));
     882             : #else
     883             :       nsCOMPtr<nsITabChild> tabChild =
     884           0 :         do_GetInterface(mDocument->DocumentNode()->GetDocShell());
     885           0 :       if (tabChild) {
     886           0 :         static_cast<TabChild*>(tabChild.get())->
     887           0 :           SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, id, 0, 0);
     888             :       }
     889             : #endif
     890             :     }
     891             :   }
     892             : 
     893           0 :   mObservingState = eRefreshObserving;
     894           0 :   if (!mDocument)
     895           0 :     return;
     896             : 
     897             :   // Stop further processing if there are no new notifications of any kind or
     898             :   // events and document load is processed.
     899           0 :   if (mContentInsertions.Count() == 0 && mNotifications.IsEmpty() &&
     900           0 :       mEvents.IsEmpty() && mTextHash.Count() == 0 &&
     901           0 :       mHangingChildDocuments.IsEmpty() &&
     902           0 :       mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) &&
     903           0 :       mPresShell->RemoveRefreshObserver(this, FlushType::Display)) {
     904           0 :     mObservingState = eNotObservingRefresh;
     905             :   }
     906             : }
     907             : 
     908             : void
     909           0 : NotificationController::EventMap::PutEvent(AccTreeMutationEvent* aEvent)
     910             : {
     911           0 :   EventType type = GetEventType(aEvent);
     912           0 :   uint64_t addr = reinterpret_cast<uintptr_t>(aEvent->GetAccessible());
     913           0 :   MOZ_ASSERT((addr & 0x3) == 0, "accessible is not 4 byte aligned");
     914           0 :   addr |= type;
     915           0 :   mTable.Put(addr, aEvent);
     916           0 : }
     917             : 
     918             : AccTreeMutationEvent*
     919           0 : NotificationController::EventMap::GetEvent(Accessible* aTarget, EventType aType)
     920             : {
     921           0 :   uint64_t addr = reinterpret_cast<uintptr_t>(aTarget);
     922           0 :   MOZ_ASSERT((addr & 0x3) == 0, "target is not 4 byte aligned");
     923             : 
     924           0 :   addr |= aType;
     925           0 :   return mTable.GetWeak(addr);
     926             : }
     927             : 
     928             : void
     929           0 : NotificationController::EventMap::RemoveEvent(AccTreeMutationEvent* aEvent)
     930             : {
     931           0 :   EventType type = GetEventType(aEvent);
     932           0 :   uint64_t addr = reinterpret_cast<uintptr_t>(aEvent->GetAccessible());
     933           0 :   MOZ_ASSERT((addr & 0x3) == 0, "accessible is not 4 byte aligned");
     934           0 :   addr |= type;
     935             : 
     936           0 :   MOZ_ASSERT(mTable.GetWeak(addr) == aEvent, "mTable has the wrong event");
     937           0 :   mTable.Remove(addr);
     938           0 : }
     939             : 
     940             :   NotificationController::EventMap::EventType
     941           0 : NotificationController::EventMap::GetEventType(AccTreeMutationEvent* aEvent)
     942             : {
     943           0 :   switch(aEvent->GetEventType())
     944             :   {
     945             :     case nsIAccessibleEvent::EVENT_SHOW:
     946           0 :       return ShowEvent;
     947             :     case nsIAccessibleEvent::EVENT_HIDE:
     948           0 :       return HideEvent;
     949             :     case nsIAccessibleEvent::EVENT_REORDER:
     950           0 :       return ReorderEvent;
     951             :     default:
     952           0 :       MOZ_ASSERT_UNREACHABLE("event has invalid type");
     953             :       return ShowEvent;
     954             :   }
     955             : }

Generated by: LCOV version 1.13