Bug 1626988 [wpt PR 22658] - wake lock: Remove WakeLockPermissionDescriptor, use...
[gecko.git] / accessible / base / EventQueue.cpp
blob71e06483efbbbaa22d210207b2905a12fb227cbc
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/. */
6 #include "EventQueue.h"
8 #include "Accessible-inl.h"
9 #include "nsEventShell.h"
10 #include "DocAccessible.h"
11 #include "DocAccessibleChild.h"
12 #include "nsAccessibilityService.h"
13 #include "nsTextEquivUtils.h"
14 #ifdef A11Y_LOG
15 # include "Logging.h"
16 #endif
18 using namespace mozilla;
19 using namespace mozilla::a11y;
21 // Defines the number of selection add/remove events in the queue when they
22 // aren't packed into single selection within event.
23 const unsigned int kSelChangeCountToPack = 5;
25 ////////////////////////////////////////////////////////////////////////////////
26 // EventQueue
27 ////////////////////////////////////////////////////////////////////////////////
29 bool EventQueue::PushEvent(AccEvent* aEvent) {
30 NS_ASSERTION((aEvent->mAccessible && aEvent->mAccessible->IsApplication()) ||
31 aEvent->Document() == mDocument,
32 "Queued event belongs to another document!");
34 if (!mEvents.AppendElement(aEvent)) return false;
36 // Filter events.
37 CoalesceEvents();
39 if (aEvent->mEventRule != AccEvent::eDoNotEmit &&
40 (aEvent->mEventType == nsIAccessibleEvent::EVENT_NAME_CHANGE ||
41 aEvent->mEventType == nsIAccessibleEvent::EVENT_TEXT_REMOVED ||
42 aEvent->mEventType == nsIAccessibleEvent::EVENT_TEXT_INSERTED)) {
43 PushNameChange(aEvent->mAccessible);
45 return true;
48 bool EventQueue::PushNameChange(Accessible* aTarget) {
49 // Fire name change event on parent given that this event hasn't been
50 // coalesced, the parent's name was calculated from its subtree, and the
51 // subtree was changed.
52 if (aTarget->HasNameDependentParent()) {
53 // Only continue traversing up the tree if it's possible that the parent
54 // accessible's name can depend on this accessible's name.
55 Accessible* parent = aTarget->Parent();
56 while (parent &&
57 nsTextEquivUtils::HasNameRule(parent, eNameFromSubtreeIfReqRule)) {
58 // Test possible name dependent parent.
59 if (nsTextEquivUtils::HasNameRule(parent, eNameFromSubtreeRule)) {
60 nsAutoString name;
61 ENameValueFlag nameFlag = parent->Name(name);
62 // If name is obtained from subtree, fire name change event.
63 if (nameFlag == eNameFromSubtree) {
64 RefPtr<AccEvent> nameChangeEvent =
65 new AccEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, parent);
66 return PushEvent(nameChangeEvent);
68 break;
70 parent = parent->Parent();
73 return false;
76 ////////////////////////////////////////////////////////////////////////////////
77 // EventQueue: private
79 void EventQueue::CoalesceEvents() {
80 NS_ASSERTION(mEvents.Length(), "There should be at least one pending event!");
81 uint32_t tail = mEvents.Length() - 1;
82 AccEvent* tailEvent = mEvents[tail];
84 switch (tailEvent->mEventRule) {
85 case AccEvent::eCoalesceReorder: {
86 DebugOnly<Accessible*> target = tailEvent->mAccessible.get();
87 MOZ_ASSERT(
88 target->IsApplication() || target->IsOuterDoc() ||
89 target->IsXULTree(),
90 "Only app or outerdoc accessible reorder events are in the queue");
91 MOZ_ASSERT(tailEvent->GetEventType() == nsIAccessibleEvent::EVENT_REORDER,
92 "only reorder events should be queued");
93 break; // case eCoalesceReorder
96 case AccEvent::eCoalesceOfSameType: {
97 // Coalesce old events by newer event.
98 for (uint32_t index = tail - 1; index < tail; index--) {
99 AccEvent* accEvent = mEvents[index];
100 if (accEvent->mEventType == tailEvent->mEventType &&
101 accEvent->mEventRule == tailEvent->mEventRule) {
102 accEvent->mEventRule = AccEvent::eDoNotEmit;
103 return;
106 break; // case eCoalesceOfSameType
109 case AccEvent::eCoalesceSelectionChange: {
110 AccSelChangeEvent* tailSelChangeEvent = downcast_accEvent(tailEvent);
111 for (uint32_t index = tail - 1; index < tail; index--) {
112 AccEvent* thisEvent = mEvents[index];
113 if (thisEvent->mEventRule == tailEvent->mEventRule) {
114 AccSelChangeEvent* thisSelChangeEvent = downcast_accEvent(thisEvent);
116 // Coalesce selection change events within same control.
117 if (tailSelChangeEvent->mWidget == thisSelChangeEvent->mWidget) {
118 CoalesceSelChangeEvents(tailSelChangeEvent, thisSelChangeEvent,
119 index);
120 return;
124 break; // eCoalesceSelectionChange
127 case AccEvent::eCoalesceStateChange: {
128 // If state change event is duped then ignore previous event. If state
129 // change event is opposite to previous event then no event is emitted
130 // (accessible state wasn't changed).
131 for (uint32_t index = tail - 1; index < tail; index--) {
132 AccEvent* thisEvent = mEvents[index];
133 if (thisEvent->mEventRule != AccEvent::eDoNotEmit &&
134 thisEvent->mEventType == tailEvent->mEventType &&
135 thisEvent->mAccessible == tailEvent->mAccessible) {
136 AccStateChangeEvent* thisSCEvent = downcast_accEvent(thisEvent);
137 AccStateChangeEvent* tailSCEvent = downcast_accEvent(tailEvent);
138 if (thisSCEvent->mState == tailSCEvent->mState) {
139 thisEvent->mEventRule = AccEvent::eDoNotEmit;
140 if (thisSCEvent->mIsEnabled != tailSCEvent->mIsEnabled)
141 tailEvent->mEventRule = AccEvent::eDoNotEmit;
145 break; // eCoalesceStateChange
148 case AccEvent::eCoalesceTextSelChange: {
149 // Coalesce older event by newer event for the same selection or target.
150 // Events for same selection may have different targets and vice versa one
151 // target may be pointed by different selections (for latter see
152 // bug 927159).
153 for (uint32_t index = tail - 1; index < tail; index--) {
154 AccEvent* thisEvent = mEvents[index];
155 if (thisEvent->mEventRule != AccEvent::eDoNotEmit &&
156 thisEvent->mEventType == tailEvent->mEventType) {
157 AccTextSelChangeEvent* thisTSCEvent = downcast_accEvent(thisEvent);
158 AccTextSelChangeEvent* tailTSCEvent = downcast_accEvent(tailEvent);
159 if (thisTSCEvent->mSel == tailTSCEvent->mSel ||
160 thisEvent->mAccessible == tailEvent->mAccessible)
161 thisEvent->mEventRule = AccEvent::eDoNotEmit;
164 break; // eCoalesceTextSelChange
167 case AccEvent::eRemoveDupes: {
168 // Check for repeat events, coalesce newly appended event by more older
169 // event.
170 for (uint32_t index = tail - 1; index < tail; index--) {
171 AccEvent* accEvent = mEvents[index];
172 if (accEvent->mEventType == tailEvent->mEventType &&
173 accEvent->mEventRule == tailEvent->mEventRule &&
174 accEvent->mAccessible == tailEvent->mAccessible) {
175 tailEvent->mEventRule = AccEvent::eDoNotEmit;
176 return;
179 break; // case eRemoveDupes
182 default:
183 break; // case eAllowDupes, eDoNotEmit
184 } // switch
187 void EventQueue::CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
188 AccSelChangeEvent* aThisEvent,
189 uint32_t aThisIndex) {
190 aTailEvent->mPreceedingCount = aThisEvent->mPreceedingCount + 1;
192 // Pack all preceding events into single selection within event
193 // when we receive too much selection add/remove events.
194 if (aTailEvent->mPreceedingCount >= kSelChangeCountToPack) {
195 aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_WITHIN;
196 aTailEvent->mAccessible = aTailEvent->mWidget;
197 aThisEvent->mEventRule = AccEvent::eDoNotEmit;
199 // Do not emit any preceding selection events for same widget if they
200 // weren't coalesced yet.
201 if (aThisEvent->mEventType != nsIAccessibleEvent::EVENT_SELECTION_WITHIN) {
202 for (uint32_t jdx = aThisIndex - 1; jdx < aThisIndex; jdx--) {
203 AccEvent* prevEvent = mEvents[jdx];
204 if (prevEvent->mEventRule == aTailEvent->mEventRule) {
205 AccSelChangeEvent* prevSelChangeEvent = downcast_accEvent(prevEvent);
206 if (prevSelChangeEvent->mWidget == aTailEvent->mWidget)
207 prevSelChangeEvent->mEventRule = AccEvent::eDoNotEmit;
211 return;
214 // Pack sequential selection remove and selection add events into
215 // single selection change event.
216 if (aTailEvent->mPreceedingCount == 1 &&
217 aTailEvent->mItem != aThisEvent->mItem) {
218 if (aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
219 aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
220 aThisEvent->mEventRule = AccEvent::eDoNotEmit;
221 aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
222 aTailEvent->mPackedEvent = aThisEvent;
223 return;
226 if (aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
227 aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
228 aTailEvent->mEventRule = AccEvent::eDoNotEmit;
229 aThisEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
230 aThisEvent->mPackedEvent = aTailEvent;
231 return;
235 // Unpack the packed selection change event because we've got one
236 // more selection add/remove.
237 if (aThisEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION) {
238 if (aThisEvent->mPackedEvent) {
239 aThisEvent->mPackedEvent->mEventType =
240 aThisEvent->mPackedEvent->mSelChangeType ==
241 AccSelChangeEvent::eSelectionAdd
242 ? nsIAccessibleEvent::EVENT_SELECTION_ADD
243 : nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
245 aThisEvent->mPackedEvent->mEventRule = AccEvent::eCoalesceSelectionChange;
247 aThisEvent->mPackedEvent = nullptr;
250 aThisEvent->mEventType =
251 aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd
252 ? nsIAccessibleEvent::EVENT_SELECTION_ADD
253 : nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
255 return;
258 // Convert into selection add since control has single selection but other
259 // selection events for this control are queued.
260 if (aTailEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION)
261 aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
264 ////////////////////////////////////////////////////////////////////////////////
265 // EventQueue: event queue
267 void EventQueue::ProcessEventQueue() {
268 // Process only currently queued events.
269 nsTArray<RefPtr<AccEvent> > events;
270 events.SwapElements(mEvents);
272 uint32_t eventCount = events.Length();
273 #ifdef A11Y_LOG
274 if (eventCount > 0 && logging::IsEnabled(logging::eEvents)) {
275 logging::MsgBegin("EVENTS", "events processing");
276 logging::Address("document", mDocument);
277 logging::MsgEnd();
279 #endif
281 for (uint32_t idx = 0; idx < eventCount; idx++) {
282 AccEvent* event = events[idx];
283 if (event->mEventRule != AccEvent::eDoNotEmit) {
284 Accessible* target = event->GetAccessible();
285 if (!target || target->IsDefunct()) continue;
287 // Dispatch the focus event if target is still focused.
288 if (event->mEventType == nsIAccessibleEvent::EVENT_FOCUS) {
289 FocusMgr()->ProcessFocusEvent(event);
290 continue;
293 // Dispatch caret moved and text selection change events.
294 if (event->mEventType ==
295 nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED) {
296 SelectionMgr()->ProcessTextSelChangeEvent(event);
297 continue;
300 // Fire selected state change events in support to selection events.
301 if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_ADD) {
302 nsEventShell::FireEvent(event->mAccessible, states::SELECTED, true,
303 event->mIsFromUserInput);
305 } else if (event->mEventType ==
306 nsIAccessibleEvent::EVENT_SELECTION_REMOVE) {
307 nsEventShell::FireEvent(event->mAccessible, states::SELECTED, false,
308 event->mIsFromUserInput);
310 } else if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION) {
311 AccSelChangeEvent* selChangeEvent = downcast_accEvent(event);
312 nsEventShell::FireEvent(event->mAccessible, states::SELECTED,
313 (selChangeEvent->mSelChangeType ==
314 AccSelChangeEvent::eSelectionAdd),
315 event->mIsFromUserInput);
317 if (selChangeEvent->mPackedEvent) {
318 nsEventShell::FireEvent(
319 selChangeEvent->mPackedEvent->mAccessible, states::SELECTED,
320 (selChangeEvent->mPackedEvent->mSelChangeType ==
321 AccSelChangeEvent::eSelectionAdd),
322 selChangeEvent->mPackedEvent->mIsFromUserInput);
326 nsEventShell::FireEvent(event);
329 if (!mDocument) return;