tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

NotificationController.h (11494B)


      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 #ifndef mozilla_a11y_NotificationController_h_
      7 #define mozilla_a11y_NotificationController_h_
      8 
      9 #include "EventQueue.h"
     10 
     11 #include "nsClassHashtable.h"
     12 #include "nsCycleCollectionParticipant.h"
     13 #include "nsIFrame.h"
     14 #include "nsRefreshObservers.h"
     15 #include "nsTHashSet.h"
     16 
     17 #include <utility>
     18 
     19 #ifdef A11Y_LOG
     20 #  include "Logging.h"
     21 #endif
     22 
     23 namespace mozilla {
     24 
     25 class PresShell;
     26 
     27 namespace a11y {
     28 
     29 class DocAccessible;
     30 
     31 /**
     32 * Notification interface.
     33 */
     34 class Notification {
     35 public:
     36  NS_INLINE_DECL_REFCOUNTING(mozilla::a11y::Notification)
     37 
     38  /**
     39   * Process notification.
     40   */
     41  virtual void Process() = 0;
     42 
     43 protected:
     44  Notification() {}
     45 
     46  /**
     47   * Protected destructor, to discourage deletion outside of Release():
     48   */
     49  virtual ~Notification() {}
     50 
     51 private:
     52  Notification(const Notification&);
     53  Notification& operator=(const Notification&);
     54 };
     55 
     56 /**
     57 * Template class for generic notification.
     58 *
     59 * @note  Instance is kept as a weak ref, the caller must guarantee it exists
     60 *        longer than the document accessible owning the notification controller
     61 *        that this notification is processed by.
     62 */
     63 template <class Class, class... Args>
     64 class TNotification : public Notification {
     65 public:
     66  typedef void (Class::*Callback)(Args*...);
     67 
     68  TNotification(Class* aInstance, Callback aCallback, Args*... aArgs)
     69      : mInstance(aInstance), mCallback(aCallback), mArgs(aArgs...) {}
     70  virtual ~TNotification() { mInstance = nullptr; }
     71 
     72  virtual void Process() override {
     73    ProcessHelper(std::index_sequence_for<Args...>{});
     74  }
     75 
     76 private:
     77  TNotification(const TNotification&);
     78  TNotification& operator=(const TNotification&);
     79 
     80  template <size_t... Indices>
     81  void ProcessHelper(std::index_sequence<Indices...>) {
     82    (mInstance->*mCallback)(std::get<Indices>(mArgs)...);
     83  }
     84 
     85  Class* mInstance;
     86  Callback mCallback;
     87  std::tuple<RefPtr<Args>...> mArgs;
     88 };
     89 
     90 /**
     91 * Used to process notifications from core for the document accessible.
     92 */
     93 class NotificationController final : public EventQueue,
     94                                     public nsARefreshObserver {
     95 public:
     96  NotificationController(DocAccessible* aDocument, PresShell* aPresShell);
     97 
     98  NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override;
     99  NS_IMETHOD_(MozExternalRefCountType) Release(void) override;
    100 
    101  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(NotificationController)
    102 
    103  /**
    104   * Shutdown the notification controller.
    105   */
    106  void Shutdown();
    107 
    108  /**
    109   * Add an accessible event into the queue to process it later.
    110   */
    111  void QueueEvent(AccEvent* aEvent) {
    112    if (PushEvent(aEvent)) {
    113      ScheduleProcessing();
    114    }
    115  }
    116 
    117  /**
    118   * Queue a mutation event to emit if not coalesced away.  Returns true if the
    119   * event was queued and has not yet been coalesced.
    120   */
    121  bool QueueMutationEvent(AccTreeMutationEvent* aEvent);
    122 
    123  /**
    124   * Coalesce all queued mutation events.
    125   */
    126  void CoalesceMutationEvents();
    127 
    128  /**
    129   * Schedule binding the child document to the tree of this document.
    130   */
    131  void ScheduleChildDocBinding(DocAccessible* aDocument);
    132 
    133  /**
    134   * Schedule the accessible tree update because of rendered text changes.
    135   */
    136  inline void ScheduleTextUpdate(nsIContent* aTextNode) {
    137    // Make sure we are not called with a node that is not in the DOM tree or
    138    // not visible.
    139    MOZ_ASSERT(aTextNode->GetParentNode(), "A text node is not in DOM");
    140    MOZ_ASSERT(aTextNode->GetPrimaryFrame(),
    141               "A text node doesn't have a frame");
    142    MOZ_ASSERT(aTextNode->GetPrimaryFrame()->StyleVisibility()->IsVisible(),
    143               "A text node is not visible");
    144 
    145    mTextArray.AppendElement(aTextNode);
    146 
    147    ScheduleProcessing();
    148  }
    149 
    150  /**
    151   * Pend accessible tree update for content insertion.
    152   */
    153  void ScheduleContentInsertion(LocalAccessible* aContainer,
    154                                nsTArray<nsCOMPtr<nsIContent>>& aInsertions);
    155 
    156  /**
    157   * Pend an accessible subtree relocation.
    158   */
    159  void ScheduleRelocation(LocalAccessible* aOwner) {
    160    if (!mRelocations.Contains(aOwner)) {
    161      // XXX(Bug 1631371) Check if this should use a fallible operation as it
    162      // pretended earlier, or change the return type to void.
    163      mRelocations.AppendElement(aOwner);
    164      ScheduleProcessing();
    165    }
    166  }
    167 
    168  /**
    169   * Start to observe refresh to make notifications and events processing after
    170   * layout.
    171   */
    172  void ScheduleProcessing();
    173 
    174  /**
    175   * Process the generic notification synchronously if there are no pending
    176   * layout changes and no notifications are pending or being processed right
    177   * now. Otherwise, queue it up to process asynchronously.
    178   *
    179   * @note  The caller must guarantee that the given instance still exists when
    180   *        the notification is processed.
    181   */
    182  template <class Class, class... Args>
    183  inline void HandleNotification(
    184      Class* aInstance,
    185      typename TNotification<Class, Args...>::Callback aMethod,
    186      Args*... aArgs) {
    187    if (!IsUpdatePending()) {
    188 #ifdef A11Y_LOG
    189      if (mozilla::a11y::logging::IsEnabled(
    190              mozilla::a11y::logging::eNotifications)) {
    191        mozilla::a11y::logging::Text("sync notification processing");
    192      }
    193 #endif
    194      (aInstance->*aMethod)(aArgs...);
    195      return;
    196    }
    197 
    198    RefPtr<Notification> notification =
    199        new TNotification<Class, Args...>(aInstance, aMethod, aArgs...);
    200    if (notification) {
    201      // XXX(Bug 1631371) Check if this should use a fallible operation as it
    202      // pretended earlier.
    203      mNotifications.AppendElement(notification);
    204      ScheduleProcessing();
    205    }
    206  }
    207 
    208  /**
    209   * Schedule the generic notification to process asynchronously.
    210   *
    211   * @note  The caller must guarantee that the given instance still exists when
    212   *        the notification is processed.
    213   */
    214  template <class Class>
    215  inline void ScheduleNotification(
    216      Class* aInstance, typename TNotification<Class>::Callback aMethod) {
    217    RefPtr<Notification> notification =
    218        new TNotification<Class>(aInstance, aMethod);
    219    if (notification) {
    220      // XXX(Bug 1631371) Check if this should use a fallible operation as it
    221      // pretended earlier.
    222      mNotifications.AppendElement(notification);
    223      ScheduleProcessing();
    224    }
    225  }
    226 
    227  template <class Class, class Arg>
    228  inline void ScheduleNotification(
    229      Class* aInstance, typename TNotification<Class, Arg>::Callback aMethod,
    230      Arg* aArg) {
    231    RefPtr<Notification> notification =
    232        new TNotification<Class, Arg>(aInstance, aMethod, aArg);
    233    if (notification) {
    234      // XXX(Bug 1631371) Check if this should use a fallible operation as it
    235      // pretended earlier.
    236      mNotifications.AppendElement(notification);
    237      ScheduleProcessing();
    238    }
    239  }
    240 
    241 #ifdef DEBUG
    242  bool IsUpdating() const {
    243    return mObservingState == eRefreshProcessingForUpdate;
    244  }
    245 #endif
    246 
    247  /**
    248   * Return true if the accessible tree state update is pending.
    249   */
    250  bool IsUpdatePending() const;
    251 
    252 protected:
    253  virtual ~NotificationController();
    254 
    255  nsCycleCollectingAutoRefCnt mRefCnt;
    256  NS_DECL_OWNINGTHREAD
    257 
    258  /**
    259   * Return true if we should wait for processing from the parent before we can
    260   * process our own queue.
    261   */
    262  bool WaitingForParent() const;
    263 
    264 private:
    265  NotificationController(const NotificationController&);
    266  NotificationController& operator=(const NotificationController&);
    267 
    268  // nsARefreshObserver
    269  virtual void WillRefresh(mozilla::TimeStamp aTime) override;
    270 
    271 private:
    272  /**
    273   * Remove a specific hide event if it should not be propagated.
    274   */
    275  void CoalesceHideEvent(AccHideEvent* aHideEvent);
    276 
    277  /**
    278   * get rid of a mutation event that is no longer necessary.
    279   */
    280  void DropMutationEvent(AccTreeMutationEvent* aEvent);
    281 
    282  /**
    283   * For content process documents:
    284   *   Assess and queue all necessary mutation events. This function queues the
    285   *   events on DocAccessibleChild. To fire the queued events, call
    286   *   DocAccessibleChild::SendQueuedMutationEvents. This function may fire
    287   *   events that must occur before mutation events.
    288   * For parent process documents:
    289   *   Fire all necessary mutation events immediately.
    290   */
    291  void ProcessMutationEvents();
    292 
    293  /**
    294   * Indicates whether we're waiting on an event queue processing from our
    295   * notification controller to flush events.
    296   */
    297  enum eObservingState {
    298    eNotObservingRefresh,
    299    eRefreshObserving,
    300    eRefreshProcessing,
    301    eRefreshProcessingForUpdate
    302  };
    303  eObservingState mObservingState;
    304 
    305  /**
    306   * The presshell of the document accessible.
    307   */
    308  PresShell* mPresShell;
    309 
    310  /**
    311   * Child documents that needs to be bound to the tree.
    312   */
    313  nsTArray<RefPtr<DocAccessible>> mHangingChildDocuments;
    314 
    315  /**
    316   * Pending accessible tree update notifications for content insertions.
    317   */
    318  nsClassHashtable<nsRefPtrHashKey<LocalAccessible>,
    319                   nsTArray<nsCOMPtr<nsIContent>>>
    320      mContentInsertions;
    321 
    322  template <class T>
    323  class nsCOMPtrHashKey : public PLDHashEntryHdr {
    324   public:
    325    typedef T* KeyType;
    326    typedef const T* KeyTypePointer;
    327 
    328    explicit nsCOMPtrHashKey(const T* aKey) : mKey(const_cast<T*>(aKey)) {}
    329    nsCOMPtrHashKey(nsCOMPtrHashKey<T>&& aOther)
    330        : PLDHashEntryHdr(std::move(aOther)), mKey(std::move(aOther.mKey)) {}
    331    ~nsCOMPtrHashKey() {}
    332 
    333    KeyType GetKey() const { return mKey; }
    334    bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; }
    335 
    336    static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
    337    static PLDHashNumber HashKey(KeyTypePointer aKey) {
    338      return NS_PTR_TO_INT32(aKey) >> 2;
    339    }
    340 
    341    enum { ALLOW_MEMMOVE = true };
    342 
    343   protected:
    344    nsCOMPtr<T> mKey;
    345  };
    346 
    347  /**
    348   * Pending accessible tree update notifications for rendered text changes.
    349   * When there are a lot of nearby text insertions (e.g. during a reflow), it
    350   * is much more performant to process them in order because we then benefit
    351   * from the layout line cursor. Therefore, we use an array here.
    352   */
    353  nsTArray<nsCOMPtr<nsIContent>> mTextArray;
    354 
    355  /**
    356   * Other notifications like DOM events. Don't make this an AutoTArray; we
    357   * use SwapElements() on it.
    358   */
    359  nsTArray<RefPtr<Notification>> mNotifications;
    360 
    361  /**
    362   * Holds all scheduled relocations.
    363   */
    364  nsTArray<RefPtr<LocalAccessible>> mRelocations;
    365 
    366  /**
    367   * A list of all mutation events we may want to emit.  Ordered from the first
    368   * event that should be emitted to the last one to emit.
    369   */
    370  RefPtr<AccTreeMutationEvent> mFirstMutationEvent;
    371  RefPtr<AccTreeMutationEvent> mLastMutationEvent;
    372 
    373  /**
    374   * A class to map an accessible and event type to an event.
    375   */
    376  class EventMap {
    377   public:
    378    enum EventType {
    379      ShowEvent = 0x0,
    380      HideEvent = 0x1,
    381      ReorderEvent = 0x2,
    382    };
    383 
    384    void PutEvent(AccTreeMutationEvent* aEvent);
    385    AccTreeMutationEvent* GetEvent(LocalAccessible* aTarget, EventType aType);
    386    void RemoveEvent(AccTreeMutationEvent* aEvent);
    387    void Clear() { mTable.Clear(); }
    388 
    389   private:
    390    EventType GetEventType(AccTreeMutationEvent* aEvent);
    391 
    392    nsRefPtrHashtable<nsUint64HashKey, AccTreeMutationEvent> mTable;
    393  };
    394 
    395  EventMap mMutationMap;
    396  uint32_t mEventGeneration;
    397 };
    398 
    399 }  // namespace a11y
    400 }  // namespace mozilla
    401 
    402 #endif  // mozilla_a11y_NotificationController_h_