Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/accessible/base/EventQueue.cpp
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
#include "EventQueue.h"
7
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
17
18
using namespace mozilla;
19
using namespace mozilla::a11y;
20
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;
24
25
////////////////////////////////////////////////////////////////////////////////
26
// EventQueue
27
////////////////////////////////////////////////////////////////////////////////
28
29
bool
30
EventQueue::PushEvent(AccEvent* aEvent)
31
0
{
32
0
  NS_ASSERTION((aEvent->mAccessible && aEvent->mAccessible->IsApplication()) ||
33
0
               aEvent->Document() == mDocument,
34
0
               "Queued event belongs to another document!");
35
0
36
0
  if (!mEvents.AppendElement(aEvent))
37
0
    return false;
38
0
39
0
  // Filter events.
40
0
  CoalesceEvents();
41
0
42
0
  if (aEvent->mEventRule != AccEvent::eDoNotEmit &&
43
0
      (aEvent->mEventType == nsIAccessibleEvent::EVENT_NAME_CHANGE ||
44
0
       aEvent->mEventType == nsIAccessibleEvent::EVENT_TEXT_REMOVED ||
45
0
       aEvent->mEventType == nsIAccessibleEvent::EVENT_TEXT_INSERTED)) {
46
0
    PushNameChange(aEvent->mAccessible);
47
0
  }
48
0
  return true;
49
0
}
50
51
bool
52
EventQueue::PushNameChange(Accessible* aTarget)
53
0
{
54
0
  // Fire name change event on parent given that this event hasn't been
55
0
  // coalesced, the parent's name was calculated from its subtree, and the
56
0
  // subtree was changed.
57
0
  if (aTarget->HasNameDependentParent()) {
58
0
    // Only continue traversing up the tree if it's possible that the parent
59
0
    // accessible's name can depend on this accessible's name.
60
0
    Accessible* parent = aTarget->Parent();
61
0
    while (parent &&
62
0
           nsTextEquivUtils::HasNameRule(parent, eNameFromSubtreeIfReqRule)) {
63
0
      // Test possible name dependent parent.
64
0
      if (nsTextEquivUtils::HasNameRule(parent, eNameFromSubtreeRule)) {
65
0
        nsAutoString name;
66
0
        ENameValueFlag nameFlag = parent->Name(name);
67
0
        // If name is obtained from subtree, fire name change event.
68
0
        if (nameFlag == eNameFromSubtree) {
69
0
          RefPtr<AccEvent> nameChangeEvent =
70
0
            new AccEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, parent);
71
0
          return PushEvent(nameChangeEvent);
72
0
        }
73
0
        break;
74
0
      }
75
0
      parent = parent->Parent();
76
0
    }
77
0
  }
78
0
  return false;
79
0
}
80
81
////////////////////////////////////////////////////////////////////////////////
82
// EventQueue: private
83
84
void
85
EventQueue::CoalesceEvents()
86
0
{
87
0
  NS_ASSERTION(mEvents.Length(), "There should be at least one pending event!");
88
0
  uint32_t tail = mEvents.Length() - 1;
89
0
  AccEvent* tailEvent = mEvents[tail];
90
0
91
0
  switch(tailEvent->mEventRule) {
92
0
    case AccEvent::eCoalesceReorder:
93
0
    {
94
0
      DebugOnly<Accessible*> target = tailEvent->mAccessible.get();
95
0
      MOZ_ASSERT(target->IsApplication() ||
96
0
                 target->IsOuterDoc() ||
97
0
                 target->IsXULTree(),
98
0
                 "Only app or outerdoc accessible reorder events are in the queue");
99
0
      MOZ_ASSERT(tailEvent->GetEventType() == nsIAccessibleEvent::EVENT_REORDER, "only reorder events should be queued");
100
0
      break; // case eCoalesceReorder
101
0
    }
102
0
103
0
    case AccEvent::eCoalesceOfSameType:
104
0
    {
105
0
      // Coalesce old events by newer event.
106
0
      for (uint32_t index = tail - 1; index < tail; index--) {
107
0
        AccEvent* accEvent = mEvents[index];
108
0
        if (accEvent->mEventType == tailEvent->mEventType &&
109
0
          accEvent->mEventRule == tailEvent->mEventRule) {
110
0
          accEvent->mEventRule = AccEvent::eDoNotEmit;
111
0
          return;
112
0
        }
113
0
      }
114
0
    } break; // case eCoalesceOfSameType
115
0
116
0
    case AccEvent::eCoalesceSelectionChange:
117
0
    {
118
0
      AccSelChangeEvent* tailSelChangeEvent = downcast_accEvent(tailEvent);
119
0
      for (uint32_t index = tail - 1; index < tail; index--) {
120
0
        AccEvent* thisEvent = mEvents[index];
121
0
        if (thisEvent->mEventRule == tailEvent->mEventRule) {
122
0
          AccSelChangeEvent* thisSelChangeEvent =
123
0
            downcast_accEvent(thisEvent);
124
0
125
0
          // Coalesce selection change events within same control.
126
0
          if (tailSelChangeEvent->mWidget == thisSelChangeEvent->mWidget) {
127
0
            CoalesceSelChangeEvents(tailSelChangeEvent, thisSelChangeEvent, index);
128
0
            return;
129
0
          }
130
0
        }
131
0
      }
132
0
133
0
    } break; // eCoalesceSelectionChange
134
0
135
0
    case AccEvent::eCoalesceStateChange:
136
0
    {
137
0
      // If state change event is duped then ignore previous event. If state
138
0
      // change event is opposite to previous event then no event is emitted
139
0
      // (accessible state wasn't changed).
140
0
      for (uint32_t index = tail - 1; index < tail; index--) {
141
0
        AccEvent* thisEvent = mEvents[index];
142
0
        if (thisEvent->mEventRule != AccEvent::eDoNotEmit &&
143
0
            thisEvent->mEventType == tailEvent->mEventType &&
144
0
            thisEvent->mAccessible == tailEvent->mAccessible) {
145
0
          AccStateChangeEvent* thisSCEvent = downcast_accEvent(thisEvent);
146
0
          AccStateChangeEvent* tailSCEvent = downcast_accEvent(tailEvent);
147
0
          if (thisSCEvent->mState == tailSCEvent->mState) {
148
0
            thisEvent->mEventRule = AccEvent::eDoNotEmit;
149
0
            if (thisSCEvent->mIsEnabled != tailSCEvent->mIsEnabled)
150
0
              tailEvent->mEventRule = AccEvent::eDoNotEmit;
151
0
          }
152
0
        }
153
0
      }
154
0
      break; // eCoalesceStateChange
155
0
    }
156
0
157
0
    case AccEvent::eCoalesceTextSelChange:
158
0
    {
159
0
      // Coalesce older event by newer event for the same selection or target.
160
0
      // Events for same selection may have different targets and vice versa one
161
0
      // target may be pointed by different selections (for latter see
162
0
      // bug 927159).
163
0
      for (uint32_t index = tail - 1; index < tail; index--) {
164
0
        AccEvent* thisEvent = mEvents[index];
165
0
        if (thisEvent->mEventRule != AccEvent::eDoNotEmit &&
166
0
            thisEvent->mEventType == tailEvent->mEventType) {
167
0
          AccTextSelChangeEvent* thisTSCEvent = downcast_accEvent(thisEvent);
168
0
          AccTextSelChangeEvent* tailTSCEvent = downcast_accEvent(tailEvent);
169
0
          if (thisTSCEvent->mSel == tailTSCEvent->mSel ||
170
0
              thisEvent->mAccessible == tailEvent->mAccessible)
171
0
            thisEvent->mEventRule = AccEvent::eDoNotEmit;
172
0
        }
173
0
174
0
      }
175
0
    } break; // eCoalesceTextSelChange
176
0
177
0
    case AccEvent::eRemoveDupes:
178
0
    {
179
0
      // Check for repeat events, coalesce newly appended event by more older
180
0
      // event.
181
0
      for (uint32_t index = tail - 1; index < tail; index--) {
182
0
        AccEvent* accEvent = mEvents[index];
183
0
        if (accEvent->mEventType == tailEvent->mEventType &&
184
0
          accEvent->mEventRule == tailEvent->mEventRule &&
185
0
          accEvent->mAccessible == tailEvent->mAccessible) {
186
0
          tailEvent->mEventRule = AccEvent::eDoNotEmit;
187
0
          return;
188
0
        }
189
0
      }
190
0
    } break; // case eRemoveDupes
191
0
192
0
    default:
193
0
      break; // case eAllowDupes, eDoNotEmit
194
0
  } // switch
195
0
}
196
197
void
198
EventQueue::CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
199
                                    AccSelChangeEvent* aThisEvent,
200
                                    uint32_t aThisIndex)
201
0
{
202
0
  aTailEvent->mPreceedingCount = aThisEvent->mPreceedingCount + 1;
203
0
204
0
  // Pack all preceding events into single selection within event
205
0
  // when we receive too much selection add/remove events.
206
0
  if (aTailEvent->mPreceedingCount >= kSelChangeCountToPack) {
207
0
    aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_WITHIN;
208
0
    aTailEvent->mAccessible = aTailEvent->mWidget;
209
0
    aThisEvent->mEventRule = AccEvent::eDoNotEmit;
210
0
211
0
    // Do not emit any preceding selection events for same widget if they
212
0
    // weren't coalesced yet.
213
0
    if (aThisEvent->mEventType != nsIAccessibleEvent::EVENT_SELECTION_WITHIN) {
214
0
      for (uint32_t jdx = aThisIndex - 1; jdx < aThisIndex; jdx--) {
215
0
        AccEvent* prevEvent = mEvents[jdx];
216
0
        if (prevEvent->mEventRule == aTailEvent->mEventRule) {
217
0
          AccSelChangeEvent* prevSelChangeEvent =
218
0
            downcast_accEvent(prevEvent);
219
0
          if (prevSelChangeEvent->mWidget == aTailEvent->mWidget)
220
0
            prevSelChangeEvent->mEventRule = AccEvent::eDoNotEmit;
221
0
        }
222
0
      }
223
0
    }
224
0
    return;
225
0
  }
226
0
227
0
  // Pack sequential selection remove and selection add events into
228
0
  // single selection change event.
229
0
  if (aTailEvent->mPreceedingCount == 1 &&
230
0
      aTailEvent->mItem != aThisEvent->mItem) {
231
0
    if (aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
232
0
        aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
233
0
      aThisEvent->mEventRule = AccEvent::eDoNotEmit;
234
0
      aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
235
0
      aTailEvent->mPackedEvent = aThisEvent;
236
0
      return;
237
0
    }
238
0
239
0
    if (aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
240
0
        aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
241
0
      aTailEvent->mEventRule = AccEvent::eDoNotEmit;
242
0
      aThisEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
243
0
      aThisEvent->mPackedEvent = aTailEvent;
244
0
      return;
245
0
    }
246
0
  }
247
0
248
0
  // Unpack the packed selection change event because we've got one
249
0
  // more selection add/remove.
250
0
  if (aThisEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION) {
251
0
    if (aThisEvent->mPackedEvent) {
252
0
      aThisEvent->mPackedEvent->mEventType =
253
0
        aThisEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ?
254
0
          nsIAccessibleEvent::EVENT_SELECTION_ADD :
255
0
          nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
256
0
257
0
      aThisEvent->mPackedEvent->mEventRule =
258
0
        AccEvent::eCoalesceSelectionChange;
259
0
260
0
      aThisEvent->mPackedEvent = nullptr;
261
0
    }
262
0
263
0
    aThisEvent->mEventType =
264
0
      aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ?
265
0
        nsIAccessibleEvent::EVENT_SELECTION_ADD :
266
0
        nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
267
0
268
0
    return;
269
0
  }
270
0
271
0
  // Convert into selection add since control has single selection but other
272
0
  // selection events for this control are queued.
273
0
  if (aTailEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION)
274
0
    aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
275
0
}
276
277
////////////////////////////////////////////////////////////////////////////////
278
// EventQueue: event queue
279
280
void
281
EventQueue::ProcessEventQueue()
282
0
{
283
0
  // Process only currently queued events.
284
0
  nsTArray<RefPtr<AccEvent> > events;
285
0
  events.SwapElements(mEvents);
286
0
287
0
  uint32_t eventCount = events.Length();
288
0
#ifdef A11Y_LOG
289
0
  if (eventCount > 0 && logging::IsEnabled(logging::eEvents)) {
290
0
    logging::MsgBegin("EVENTS", "events processing");
291
0
    logging::Address("document", mDocument);
292
0
    logging::MsgEnd();
293
0
  }
294
0
#endif
295
0
296
0
  for (uint32_t idx = 0; idx < eventCount; idx++) {
297
0
    AccEvent* event = events[idx];
298
0
    if (event->mEventRule != AccEvent::eDoNotEmit) {
299
0
      Accessible* target = event->GetAccessible();
300
0
      if (!target || target->IsDefunct())
301
0
        continue;
302
0
303
0
      // Dispatch the focus event if target is still focused.
304
0
      if (event->mEventType == nsIAccessibleEvent::EVENT_FOCUS) {
305
0
        FocusMgr()->ProcessFocusEvent(event);
306
0
        continue;
307
0
      }
308
0
309
0
      // Dispatch caret moved and text selection change events.
310
0
      if (event->mEventType == nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED) {
311
0
        SelectionMgr()->ProcessTextSelChangeEvent(event);
312
0
        continue;
313
0
      }
314
0
315
0
      // Fire selected state change events in support to selection events.
316
0
      if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_ADD) {
317
0
        nsEventShell::FireEvent(event->mAccessible, states::SELECTED,
318
0
                                true, event->mIsFromUserInput);
319
0
320
0
      } else if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_REMOVE) {
321
0
        nsEventShell::FireEvent(event->mAccessible, states::SELECTED,
322
0
                                false, event->mIsFromUserInput);
323
0
324
0
      } else if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION) {
325
0
        AccSelChangeEvent* selChangeEvent = downcast_accEvent(event);
326
0
        nsEventShell::FireEvent(event->mAccessible, states::SELECTED,
327
0
                                (selChangeEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd),
328
0
                                event->mIsFromUserInput);
329
0
330
0
        if (selChangeEvent->mPackedEvent) {
331
0
          nsEventShell::FireEvent(selChangeEvent->mPackedEvent->mAccessible,
332
0
                                  states::SELECTED,
333
0
                                  (selChangeEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd),
334
0
                                  selChangeEvent->mPackedEvent->mIsFromUserInput);
335
0
        }
336
0
      }
337
0
338
0
      nsEventShell::FireEvent(event);
339
0
    }
340
0
341
0
    if (!mDocument)
342
0
      return;
343
0
  }
344
0
}