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_