Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/accessible/base/NotificationController.h
Line
Count
Source (jump to first uncovered line)
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
#include "EventTree.h"
11
12
#include "mozilla/Tuple.h"
13
#include "nsCycleCollectionParticipant.h"
14
#include "nsRefreshDriver.h"
15
16
#include <utility>
17
18
#ifdef A11Y_LOG
19
#include "Logging.h"
20
#endif
21
22
namespace mozilla {
23
namespace a11y {
24
25
class DocAccessible;
26
27
/**
28
 * Notification interface.
29
 */
30
class Notification
31
{
32
public:
33
  NS_INLINE_DECL_REFCOUNTING(mozilla::a11y::Notification)
34
35
  /**
36
   * Process notification.
37
   */
38
  virtual void Process() = 0;
39
40
protected:
41
0
  Notification() { }
42
43
  /**
44
   * Protected destructor, to discourage deletion outside of Release():
45
   */
46
0
  virtual ~Notification() { }
47
48
private:
49
  Notification(const Notification&);
50
  Notification& operator = (const Notification&);
51
};
52
53
54
/**
55
 * Template class for generic notification.
56
 *
57
 * @note  Instance is kept as a weak ref, the caller must guarantee it exists
58
 *        longer than the document accessible owning the notification controller
59
 *        that this notification is processed by.
60
 */
61
template<class Class, class ... Args>
62
class TNotification : public Notification
63
{
64
public:
65
  typedef void (Class::*Callback)(Args* ...);
66
67
  TNotification(Class* aInstance, Callback aCallback, Args* ... aArgs) :
68
0
    mInstance(aInstance), mCallback(aCallback), mArgs(aArgs...) { }
Unexecuted instantiation: mozilla::a11y::TNotification<mozilla::a11y::FocusManager, nsINode>::TNotification(mozilla::a11y::FocusManager*, void (mozilla::a11y::FocusManager::*)(nsINode*), nsINode*)
Unexecuted instantiation: mozilla::a11y::TNotification<mozilla::a11y::SelectionManager, mozilla::a11y::SelData>::TNotification(mozilla::a11y::SelectionManager*, void (mozilla::a11y::SelectionManager::*)(mozilla::a11y::SelData*), mozilla::a11y::SelData*)
Unexecuted instantiation: mozilla::a11y::TNotification<mozilla::a11y::DocAccessible, mozilla::a11y::Accessible>::TNotification(mozilla::a11y::DocAccessible*, void (mozilla::a11y::DocAccessible::*)(mozilla::a11y::Accessible*), mozilla::a11y::Accessible*)
Unexecuted instantiation: mozilla::a11y::TNotification<mozilla::a11y::RootAccessible, mozilla::dom::Event>::TNotification(mozilla::a11y::RootAccessible*, void (mozilla::a11y::RootAccessible::*)(mozilla::dom::Event*), mozilla::dom::Event*)
69
0
  virtual ~TNotification() { mInstance = nullptr; }
Unexecuted instantiation: mozilla::a11y::TNotification<mozilla::a11y::FocusManager, nsINode>::~TNotification()
Unexecuted instantiation: mozilla::a11y::TNotification<mozilla::a11y::SelectionManager, mozilla::a11y::SelData>::~TNotification()
Unexecuted instantiation: mozilla::a11y::TNotification<mozilla::a11y::DocAccessible, mozilla::a11y::Accessible>::~TNotification()
Unexecuted instantiation: mozilla::a11y::TNotification<mozilla::a11y::RootAccessible, mozilla::dom::Event>::~TNotification()
70
71
  virtual void Process() override
72
0
    { ProcessHelper(std::index_sequence_for<Args...>{}); }
Unexecuted instantiation: mozilla::a11y::TNotification<mozilla::a11y::FocusManager, nsINode>::Process()
Unexecuted instantiation: mozilla::a11y::TNotification<mozilla::a11y::SelectionManager, mozilla::a11y::SelData>::Process()
Unexecuted instantiation: mozilla::a11y::TNotification<mozilla::a11y::DocAccessible, mozilla::a11y::Accessible>::Process()
Unexecuted instantiation: mozilla::a11y::TNotification<mozilla::a11y::RootAccessible, mozilla::dom::Event>::Process()
73
74
private:
75
  TNotification(const TNotification&);
76
  TNotification& operator = (const TNotification&);
77
78
  template <size_t... Indices>
79
    void ProcessHelper(std::index_sequence<Indices...>)
80
0
  {
81
0
     (mInstance->*mCallback)(Get<Indices>(mArgs)...);
82
0
  }
Unexecuted instantiation: void mozilla::a11y::TNotification<mozilla::a11y::FocusManager, nsINode>::ProcessHelper<0ul>(std::__1::integer_sequence<unsigned long, 0ul>)
Unexecuted instantiation: void mozilla::a11y::TNotification<mozilla::a11y::SelectionManager, mozilla::a11y::SelData>::ProcessHelper<0ul>(std::__1::integer_sequence<unsigned long, 0ul>)
Unexecuted instantiation: void mozilla::a11y::TNotification<mozilla::a11y::DocAccessible, mozilla::a11y::Accessible>::ProcessHelper<0ul>(std::__1::integer_sequence<unsigned long, 0ul>)
Unexecuted instantiation: void mozilla::a11y::TNotification<mozilla::a11y::RootAccessible, mozilla::dom::Event>::ProcessHelper<0ul>(std::__1::integer_sequence<unsigned long, 0ul>)
83
84
  Class* mInstance;
85
  Callback mCallback;
86
  Tuple<RefPtr<Args> ...> mArgs;
87
};
88
89
/**
90
 * Used to process notifications from core for the document accessible.
91
 */
92
class NotificationController final : public EventQueue,
93
                                     public nsARefreshObserver
94
{
95
public:
96
  NotificationController(DocAccessible* aDocument, nsIPresShell* 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
0
  {
113
0
    if (PushEvent(aEvent)) {
114
0
      ScheduleProcessing();
115
0
    }
116
0
  }
117
118
  /**
119
   * Creates and adds a name change event into the queue for a container of
120
   * the given accessible, if the accessible is a part of name computation of
121
   * the container.
122
   */
123
  void QueueNameChange(Accessible* aChangeTarget)
124
0
  {
125
0
    if (PushNameChange(aChangeTarget)) {
126
0
      ScheduleProcessing();
127
0
    }
128
0
  }
129
130
  /**
131
   * Returns existing event tree for the given the accessible or creates one if
132
   * it doesn't exists yet.
133
   */
134
  EventTree* QueueMutation(Accessible* aContainer);
135
136
  class MoveGuard final {
137
  public:
138
    explicit MoveGuard(NotificationController* aController) :
139
      mController(aController)
140
0
    {
141
#ifdef DEBUG
142
      MOZ_ASSERT(!mController->mMoveGuardOnStack,
143
                 "Move guard is on stack already!");
144
      mController->mMoveGuardOnStack = true;
145
#endif
146
    }
147
0
    ~MoveGuard() {
148
#ifdef DEBUG
149
      MOZ_ASSERT(mController->mMoveGuardOnStack, "No move guard on stack!");
150
      mController->mMoveGuardOnStack = false;
151
#endif
152
      mController->mPrecedingEvents.Clear();
153
0
    }
154
155
  private:
156
    NotificationController* mController;
157
  };
158
159
#ifdef A11Y_LOG
160
0
  const EventTree& RootEventTree() const { return mEventTree; };
161
#endif
162
163
  /**
164
   * Queue a mutation event to emit if not coalesced away.  Returns true if the
165
   * event was queued and has not yet been coalesced.
166
   */
167
  bool QueueMutationEvent(AccTreeMutationEvent* aEvent);
168
169
  /**
170
   * Coalesce all queued mutation events.
171
   */
172
  void CoalesceMutationEvents();
173
174
  /**
175
   * Schedule binding the child document to the tree of this document.
176
   */
177
  void ScheduleChildDocBinding(DocAccessible* aDocument);
178
179
  /**
180
   * Schedule the accessible tree update because of rendered text changes.
181
   */
182
  inline void ScheduleTextUpdate(nsIContent* aTextNode)
183
0
  {
184
0
    // Make sure we are not called with a node that is not in the DOM tree or
185
0
    // not visible.
186
0
    MOZ_ASSERT(aTextNode->GetParentNode(), "A text node is not in DOM");
187
0
    MOZ_ASSERT(aTextNode->GetPrimaryFrame(), "A text node doesn't have a frame");
188
0
    MOZ_ASSERT(aTextNode->GetPrimaryFrame()->StyleVisibility()->IsVisible(),
189
0
               "A text node is not visible");
190
0
191
0
    mTextHash.PutEntry(aTextNode);
192
0
    ScheduleProcessing();
193
0
  }
194
195
  /**
196
   * Pend accessible tree update for content insertion.
197
   */
198
  void ScheduleContentInsertion(Accessible* aContainer,
199
                                nsIContent* aStartChildNode,
200
                                nsIContent* aEndChildNode);
201
202
  /**
203
   * Pend an accessible subtree relocation.
204
   */
205
  void ScheduleRelocation(Accessible* aOwner)
206
0
  {
207
0
    if (!mRelocations.Contains(aOwner) && mRelocations.AppendElement(aOwner)) {
208
0
      ScheduleProcessing();
209
0
    }
210
0
  }
211
212
  /**
213
   * Start to observe refresh to make notifications and events processing after
214
   * layout.
215
   */
216
  void ScheduleProcessing();
217
218
  /**
219
   * Process the generic notification synchronously if there are no pending
220
   * layout changes and no notifications are pending or being processed right
221
   * now. Otherwise, queue it up to process asynchronously.
222
   *
223
   * @note  The caller must guarantee that the given instance still exists when
224
   *        the notification is processed.
225
   */
226
  template<class Class, class Arg>
227
  inline void HandleNotification(Class* aInstance,
228
                                 typename TNotification<Class, Arg>::Callback aMethod,
229
                                 Arg* aArg)
230
0
  {
231
0
    if (!IsUpdatePending()) {
232
0
#ifdef A11Y_LOG
233
0
      if (mozilla::a11y::logging::IsEnabled(mozilla::a11y::logging::eNotifications))
234
0
        mozilla::a11y::logging::Text("sync notification processing");
235
0
#endif
236
0
      (aInstance->*aMethod)(aArg);
237
0
      return;
238
0
    }
239
0
240
0
    RefPtr<Notification> notification =
241
0
      new TNotification<Class, Arg>(aInstance, aMethod, aArg);
242
0
    if (notification && mNotifications.AppendElement(notification))
243
0
      ScheduleProcessing();
244
0
  }
Unexecuted instantiation: void mozilla::a11y::NotificationController::HandleNotification<mozilla::a11y::FocusManager, nsINode>(mozilla::a11y::FocusManager*, mozilla::a11y::TNotification<mozilla::a11y::FocusManager, nsINode>::Callback, nsINode*)
Unexecuted instantiation: void mozilla::a11y::NotificationController::HandleNotification<mozilla::a11y::SelectionManager, mozilla::a11y::SelData>(mozilla::a11y::SelectionManager*, mozilla::a11y::TNotification<mozilla::a11y::SelectionManager, mozilla::a11y::SelData>::Callback, mozilla::a11y::SelData*)
Unexecuted instantiation: void mozilla::a11y::NotificationController::HandleNotification<mozilla::a11y::RootAccessible, mozilla::dom::Event>(mozilla::a11y::RootAccessible*, mozilla::a11y::TNotification<mozilla::a11y::RootAccessible, mozilla::dom::Event>::Callback, mozilla::dom::Event*)
245
246
  /**
247
   * Schedule the generic notification to process asynchronously.
248
   *
249
   * @note  The caller must guarantee that the given instance still exists when
250
   *        the notification is processed.
251
   */
252
  template<class Class>
253
  inline void ScheduleNotification(Class* aInstance,
254
                                   typename TNotification<Class>::Callback aMethod)
255
  {
256
    RefPtr<Notification> notification =
257
      new TNotification<Class>(aInstance, aMethod);
258
    if (notification && mNotifications.AppendElement(notification))
259
      ScheduleProcessing();
260
  }
261
262
  template<class Class, class Arg>
263
  inline void ScheduleNotification(Class* aInstance,
264
                                   typename TNotification<Class, Arg>::Callback aMethod,
265
                                   Arg* aArg)
266
0
  {
267
0
    RefPtr<Notification> notification =
268
0
      new TNotification<Class, Arg>(aInstance, aMethod, aArg);
269
0
    if (notification && mNotifications.AppendElement(notification)) {
270
0
      ScheduleProcessing();
271
0
    }
272
0
  }
273
274
#ifdef DEBUG
275
  bool IsUpdating() const
276
    { return mObservingState == eRefreshProcessingForUpdate; }
277
#endif
278
279
protected:
280
  virtual ~NotificationController();
281
282
  nsCycleCollectingAutoRefCnt mRefCnt;
283
  NS_DECL_OWNINGTHREAD
284
285
  /**
286
   * Return true if the accessible tree state update is pending.
287
   */
288
  bool IsUpdatePending();
289
290
  /**
291
   * Return true if we should wait for processing from the parent before we can
292
   * process our own queue.
293
   */
294
  bool WaitingForParent();
295
296
private:
297
  NotificationController(const NotificationController&);
298
  NotificationController& operator = (const NotificationController&);
299
300
  // nsARefreshObserver
301
  virtual void WillRefresh(mozilla::TimeStamp aTime) override;
302
303
  /**
304
   * Set and returns a hide event, paired with a show event, for the move.
305
   */
306
  void WithdrawPrecedingEvents(nsTArray<RefPtr<AccHideEvent>>* aEvs)
307
0
  {
308
0
    if (mPrecedingEvents.Length() > 0) {
309
0
      aEvs->AppendElements(std::move(mPrecedingEvents));
310
0
    }
311
0
  }
312
  void StorePrecedingEvent(AccHideEvent* aEv)
313
0
  {
314
0
    MOZ_ASSERT(mMoveGuardOnStack, "No move guard on stack!");
315
0
    mPrecedingEvents.AppendElement(aEv);
316
0
  }
317
  void StorePrecedingEvents(nsTArray<RefPtr<AccHideEvent>>&& aEvs)
318
0
  {
319
0
    MOZ_ASSERT(mMoveGuardOnStack, "No move guard on stack!");
320
0
    mPrecedingEvents.InsertElementsAt(0, aEvs);
321
0
  }
322
323
private:
324
  /**
325
   * get rid of a mutation event that is no longer necessary.
326
   */
327
  void DropMutationEvent(AccTreeMutationEvent* aEvent);
328
329
  /**
330
   * Fire all necessary mutation events.
331
   */
332
  void ProcessMutationEvents();
333
334
  /**
335
   * Indicates whether we're waiting on an event queue processing from our
336
   * notification controller to flush events.
337
   */
338
  enum eObservingState {
339
    eNotObservingRefresh,
340
    eRefreshObserving,
341
    eRefreshProcessing,
342
    eRefreshProcessingForUpdate
343
  };
344
  eObservingState mObservingState;
345
346
  /**
347
   * The presshell of the document accessible.
348
   */
349
  nsIPresShell* mPresShell;
350
351
  /**
352
   * Child documents that needs to be bound to the tree.
353
   */
354
  nsTArray<RefPtr<DocAccessible> > mHangingChildDocuments;
355
356
  /**
357
   * Pending accessible tree update notifications for content insertions.
358
   */
359
  nsClassHashtable<nsRefPtrHashKey<Accessible>,
360
                   nsTArray<nsCOMPtr<nsIContent>>> mContentInsertions;
361
362
  template<class T>
363
  class nsCOMPtrHashKey : public PLDHashEntryHdr
364
  {
365
  public:
366
    typedef T* KeyType;
367
    typedef const T* KeyTypePointer;
368
369
0
    explicit nsCOMPtrHashKey(const T* aKey) : mKey(const_cast<T*>(aKey)) {}
370
    nsCOMPtrHashKey(nsCOMPtrHashKey<T>&& aOther)
371
      : PLDHashEntryHdr(std::move(aOther))
372
      , mKey(std::move(aOther.mKey))
373
    {}
374
0
    ~nsCOMPtrHashKey() { }
375
376
0
    KeyType GetKey() const { return mKey; }
377
0
    bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; }
378
379
0
    static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
380
    static PLDHashNumber HashKey(KeyTypePointer aKey)
381
0
      { return NS_PTR_TO_INT32(aKey) >> 2; }
382
383
    enum { ALLOW_MEMMOVE = true };
384
385
   protected:
386
     nsCOMPtr<T> mKey;
387
  };
388
389
  /**
390
   * Pending accessible tree update notifications for rendered text changes.
391
   */
392
  nsTHashtable<nsCOMPtrHashKey<nsIContent> > mTextHash;
393
394
  /**
395
   * Other notifications like DOM events. Don't make this an AutoTArray; we
396
   * use SwapElements() on it.
397
   */
398
  nsTArray<RefPtr<Notification> > mNotifications;
399
400
  /**
401
   * Holds all scheduled relocations.
402
   */
403
  nsTArray<RefPtr<Accessible> > mRelocations;
404
405
  /**
406
   * Holds all mutation events.
407
   */
408
  EventTree mEventTree;
409
410
  /**
411
   * A temporary collection of hide events that should be fired before related
412
   * show event. Used by EventTree.
413
   */
414
  nsTArray<RefPtr<AccHideEvent>> mPrecedingEvents;
415
416
#ifdef DEBUG
417
  bool mMoveGuardOnStack;
418
#endif
419
420
  friend class MoveGuard;
421
  friend class EventTree;
422
423
  /**
424
   * A list of all mutation events we may want to emit.  Ordered from the first
425
   * event that should be emitted to the last one to emit.
426
   */
427
  RefPtr<AccTreeMutationEvent> mFirstMutationEvent;
428
  RefPtr<AccTreeMutationEvent> mLastMutationEvent;
429
430
  /**
431
   * A class to map an accessible and event type to an event.
432
   */
433
  class EventMap
434
  {
435
  public:
436
    enum EventType
437
    {
438
      ShowEvent = 0x0,
439
      HideEvent = 0x1,
440
      ReorderEvent = 0x2,
441
    };
442
443
    void PutEvent(AccTreeMutationEvent* aEvent);
444
    AccTreeMutationEvent* GetEvent(Accessible* aTarget, EventType aType);
445
    void RemoveEvent(AccTreeMutationEvent* aEvent);
446
0
    void Clear() { mTable.Clear(); }
447
448
  private:
449
    EventType GetEventType(AccTreeMutationEvent* aEvent);
450
451
    nsRefPtrHashtable<nsUint64HashKey, AccTreeMutationEvent> mTable;
452
  };
453
454
  EventMap mMutationMap;
455
  uint32_t mEventGeneration;
456
};
457
458
} // namespace a11y
459
} // namespace mozilla
460
461
#endif // mozilla_a11y_NotificationController_h_