|
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 "nsAccessibilityService.h" |
|
12 #include "nsTextEquivUtils.h" |
|
13 #ifdef A11Y_LOG |
|
14 #include "Logging.h" |
|
15 #endif |
|
16 |
|
17 using namespace mozilla; |
|
18 using namespace mozilla::a11y; |
|
19 |
|
20 // Defines the number of selection add/remove events in the queue when they |
|
21 // aren't packed into single selection within event. |
|
22 const unsigned int kSelChangeCountToPack = 5; |
|
23 |
|
24 //////////////////////////////////////////////////////////////////////////////// |
|
25 // EventQueue |
|
26 //////////////////////////////////////////////////////////////////////////////// |
|
27 |
|
28 bool |
|
29 EventQueue::PushEvent(AccEvent* aEvent) |
|
30 { |
|
31 NS_ASSERTION((aEvent->mAccessible && aEvent->mAccessible->IsApplication()) || |
|
32 aEvent->GetDocAccessible() == mDocument, |
|
33 "Queued event belongs to another document!"); |
|
34 |
|
35 if (!mEvents.AppendElement(aEvent)) |
|
36 return false; |
|
37 |
|
38 // Filter events. |
|
39 CoalesceEvents(); |
|
40 |
|
41 // Fire name change event on parent given that this event hasn't been |
|
42 // coalesced, the parent's name was calculated from its subtree, and the |
|
43 // subtree was changed. |
|
44 Accessible* target = aEvent->mAccessible; |
|
45 if (aEvent->mEventRule != AccEvent::eDoNotEmit && |
|
46 target->HasNameDependentParent() && |
|
47 (aEvent->mEventType == nsIAccessibleEvent::EVENT_NAME_CHANGE || |
|
48 aEvent->mEventType == nsIAccessibleEvent::EVENT_TEXT_REMOVED || |
|
49 aEvent->mEventType == nsIAccessibleEvent::EVENT_TEXT_INSERTED || |
|
50 aEvent->mEventType == nsIAccessibleEvent::EVENT_SHOW || |
|
51 aEvent->mEventType == nsIAccessibleEvent::EVENT_HIDE)) { |
|
52 // Only continue traversing up the tree if it's possible that the parent |
|
53 // accessible's name can depend on this accessible's name. |
|
54 Accessible* parent = target->Parent(); |
|
55 while (parent && |
|
56 nsTextEquivUtils::HasNameRule(parent, eNameFromSubtreeIfReqRule)) { |
|
57 // Test possible name dependent parent. |
|
58 if (nsTextEquivUtils::HasNameRule(parent, eNameFromSubtreeRule)) { |
|
59 nsAutoString name; |
|
60 ENameValueFlag nameFlag = parent->Name(name); |
|
61 // If name is obtained from subtree, fire name change event. |
|
62 if (nameFlag == eNameFromSubtree) { |
|
63 nsRefPtr<AccEvent> nameChangeEvent = |
|
64 new AccEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, parent); |
|
65 PushEvent(nameChangeEvent); |
|
66 } |
|
67 break; |
|
68 } |
|
69 parent = parent->Parent(); |
|
70 } |
|
71 } |
|
72 |
|
73 // Associate text change with hide event if it wasn't stolen from hiding |
|
74 // siblings during coalescence. |
|
75 AccMutationEvent* showOrHideEvent = downcast_accEvent(aEvent); |
|
76 if (showOrHideEvent && !showOrHideEvent->mTextChangeEvent) |
|
77 CreateTextChangeEventFor(showOrHideEvent); |
|
78 |
|
79 return true; |
|
80 } |
|
81 |
|
82 //////////////////////////////////////////////////////////////////////////////// |
|
83 // EventQueue: private |
|
84 |
|
85 void |
|
86 EventQueue::CoalesceEvents() |
|
87 { |
|
88 NS_ASSERTION(mEvents.Length(), "There should be at least one pending event!"); |
|
89 uint32_t tail = mEvents.Length() - 1; |
|
90 AccEvent* tailEvent = mEvents[tail]; |
|
91 |
|
92 switch(tailEvent->mEventRule) { |
|
93 case AccEvent::eCoalesceReorder: |
|
94 CoalesceReorderEvents(tailEvent); |
|
95 break; // case eCoalesceReorder |
|
96 |
|
97 case AccEvent::eCoalesceMutationTextChange: |
|
98 { |
|
99 for (uint32_t index = tail - 1; index < tail; index--) { |
|
100 AccEvent* thisEvent = mEvents[index]; |
|
101 if (thisEvent->mEventRule != tailEvent->mEventRule) |
|
102 continue; |
|
103 |
|
104 // We don't currently coalesce text change events from show/hide events. |
|
105 if (thisEvent->mEventType != tailEvent->mEventType) |
|
106 continue; |
|
107 |
|
108 // Show events may be duped because of reinsertion (removal is ignored |
|
109 // because initial insertion is not processed). Ignore initial |
|
110 // insertion. |
|
111 if (thisEvent->mAccessible == tailEvent->mAccessible) |
|
112 thisEvent->mEventRule = AccEvent::eDoNotEmit; |
|
113 |
|
114 AccMutationEvent* tailMutationEvent = downcast_accEvent(tailEvent); |
|
115 AccMutationEvent* thisMutationEvent = downcast_accEvent(thisEvent); |
|
116 if (tailMutationEvent->mParent != thisMutationEvent->mParent) |
|
117 continue; |
|
118 |
|
119 // Coalesce text change events for hide and show events. |
|
120 if (thisMutationEvent->IsHide()) { |
|
121 AccHideEvent* tailHideEvent = downcast_accEvent(tailEvent); |
|
122 AccHideEvent* thisHideEvent = downcast_accEvent(thisEvent); |
|
123 CoalesceTextChangeEventsFor(tailHideEvent, thisHideEvent); |
|
124 break; |
|
125 } |
|
126 |
|
127 AccShowEvent* tailShowEvent = downcast_accEvent(tailEvent); |
|
128 AccShowEvent* thisShowEvent = downcast_accEvent(thisEvent); |
|
129 CoalesceTextChangeEventsFor(tailShowEvent, thisShowEvent); |
|
130 break; |
|
131 } |
|
132 } break; // case eCoalesceMutationTextChange |
|
133 |
|
134 case AccEvent::eCoalesceOfSameType: |
|
135 { |
|
136 // Coalesce old events by newer event. |
|
137 for (uint32_t index = tail - 1; index < tail; index--) { |
|
138 AccEvent* accEvent = mEvents[index]; |
|
139 if (accEvent->mEventType == tailEvent->mEventType && |
|
140 accEvent->mEventRule == tailEvent->mEventRule) { |
|
141 accEvent->mEventRule = AccEvent::eDoNotEmit; |
|
142 return; |
|
143 } |
|
144 } |
|
145 } break; // case eCoalesceOfSameType |
|
146 |
|
147 case AccEvent::eCoalesceSelectionChange: |
|
148 { |
|
149 AccSelChangeEvent* tailSelChangeEvent = downcast_accEvent(tailEvent); |
|
150 for (uint32_t index = tail - 1; index < tail; index--) { |
|
151 AccEvent* thisEvent = mEvents[index]; |
|
152 if (thisEvent->mEventRule == tailEvent->mEventRule) { |
|
153 AccSelChangeEvent* thisSelChangeEvent = |
|
154 downcast_accEvent(thisEvent); |
|
155 |
|
156 // Coalesce selection change events within same control. |
|
157 if (tailSelChangeEvent->mWidget == thisSelChangeEvent->mWidget) { |
|
158 CoalesceSelChangeEvents(tailSelChangeEvent, thisSelChangeEvent, index); |
|
159 return; |
|
160 } |
|
161 } |
|
162 } |
|
163 |
|
164 } break; // eCoalesceSelectionChange |
|
165 |
|
166 case AccEvent::eCoalesceStateChange: |
|
167 { |
|
168 // If state change event is duped then ignore previous event. If state |
|
169 // change event is opposite to previous event then no event is emitted |
|
170 // (accessible state wasn't changed). |
|
171 for (uint32_t index = tail - 1; index < tail; index--) { |
|
172 AccEvent* thisEvent = mEvents[index]; |
|
173 if (thisEvent->mEventRule != AccEvent::eDoNotEmit && |
|
174 thisEvent->mEventType == tailEvent->mEventType && |
|
175 thisEvent->mAccessible == tailEvent->mAccessible) { |
|
176 AccStateChangeEvent* thisSCEvent = downcast_accEvent(thisEvent); |
|
177 AccStateChangeEvent* tailSCEvent = downcast_accEvent(tailEvent); |
|
178 if (thisSCEvent->mState == tailSCEvent->mState) { |
|
179 thisEvent->mEventRule = AccEvent::eDoNotEmit; |
|
180 if (thisSCEvent->mIsEnabled != tailSCEvent->mIsEnabled) |
|
181 tailEvent->mEventRule = AccEvent::eDoNotEmit; |
|
182 } |
|
183 } |
|
184 } |
|
185 break; // eCoalesceStateChange |
|
186 } |
|
187 |
|
188 case AccEvent::eCoalesceTextSelChange: |
|
189 { |
|
190 // Coalesce older event by newer event for the same selection or target. |
|
191 // Events for same selection may have different targets and vice versa one |
|
192 // target may be pointed by different selections (for latter see |
|
193 // bug 927159). |
|
194 for (uint32_t index = tail - 1; index < tail; index--) { |
|
195 AccEvent* thisEvent = mEvents[index]; |
|
196 if (thisEvent->mEventRule != AccEvent::eDoNotEmit && |
|
197 thisEvent->mEventType == tailEvent->mEventType) { |
|
198 AccTextSelChangeEvent* thisTSCEvent = downcast_accEvent(thisEvent); |
|
199 AccTextSelChangeEvent* tailTSCEvent = downcast_accEvent(tailEvent); |
|
200 if (thisTSCEvent->mSel == tailTSCEvent->mSel || |
|
201 thisEvent->mAccessible == tailEvent->mAccessible) |
|
202 thisEvent->mEventRule = AccEvent::eDoNotEmit; |
|
203 } |
|
204 |
|
205 } |
|
206 } break; // eCoalesceTextSelChange |
|
207 |
|
208 case AccEvent::eRemoveDupes: |
|
209 { |
|
210 // Check for repeat events, coalesce newly appended event by more older |
|
211 // event. |
|
212 for (uint32_t index = tail - 1; index < tail; index--) { |
|
213 AccEvent* accEvent = mEvents[index]; |
|
214 if (accEvent->mEventType == tailEvent->mEventType && |
|
215 accEvent->mEventRule == tailEvent->mEventRule && |
|
216 accEvent->mAccessible == tailEvent->mAccessible) { |
|
217 tailEvent->mEventRule = AccEvent::eDoNotEmit; |
|
218 return; |
|
219 } |
|
220 } |
|
221 } break; // case eRemoveDupes |
|
222 |
|
223 default: |
|
224 break; // case eAllowDupes, eDoNotEmit |
|
225 } // switch |
|
226 } |
|
227 |
|
228 void |
|
229 EventQueue::CoalesceReorderEvents(AccEvent* aTailEvent) |
|
230 { |
|
231 uint32_t count = mEvents.Length(); |
|
232 for (uint32_t index = count - 2; index < count; index--) { |
|
233 AccEvent* thisEvent = mEvents[index]; |
|
234 |
|
235 // Skip events of different types and targeted to application accessible. |
|
236 if (thisEvent->mEventType != aTailEvent->mEventType || |
|
237 thisEvent->mAccessible->IsApplication()) |
|
238 continue; |
|
239 |
|
240 // If thisEvent target is not in document longer, i.e. if it was |
|
241 // removed from the tree then do not emit the event. |
|
242 if (!thisEvent->mAccessible->IsDoc() && |
|
243 !thisEvent->mAccessible->IsInDocument()) { |
|
244 thisEvent->mEventRule = AccEvent::eDoNotEmit; |
|
245 continue; |
|
246 } |
|
247 |
|
248 // Coalesce earlier event of the same target. |
|
249 if (thisEvent->mAccessible == aTailEvent->mAccessible) { |
|
250 if (thisEvent->mEventRule == AccEvent::eDoNotEmit) { |
|
251 AccReorderEvent* tailReorder = downcast_accEvent(aTailEvent); |
|
252 tailReorder->DoNotEmitAll(); |
|
253 } else { |
|
254 thisEvent->mEventRule = AccEvent::eDoNotEmit; |
|
255 } |
|
256 |
|
257 return; |
|
258 } |
|
259 |
|
260 // If tailEvent contains thisEvent |
|
261 // then |
|
262 // if show or hide of tailEvent contains a grand parent of thisEvent |
|
263 // then ignore thisEvent and its show and hide events |
|
264 // otherwise ignore thisEvent but not its show and hide events |
|
265 Accessible* thisParent = thisEvent->mAccessible; |
|
266 while (thisParent && thisParent != mDocument) { |
|
267 if (thisParent->Parent() == aTailEvent->mAccessible) { |
|
268 AccReorderEvent* tailReorder = downcast_accEvent(aTailEvent); |
|
269 uint32_t eventType = tailReorder->IsShowHideEventTarget(thisParent); |
|
270 |
|
271 // Sometimes InvalidateChildren() and |
|
272 // DocAccessible::CacheChildrenInSubtree() can conspire to reparent an |
|
273 // accessible in this case no need for mutation events. Se bug 883708 |
|
274 // for details. |
|
275 if (eventType == nsIAccessibleEvent::EVENT_SHOW || |
|
276 eventType == nsIAccessibleEvent::EVENT_HIDE) { |
|
277 AccReorderEvent* thisReorder = downcast_accEvent(thisEvent); |
|
278 thisReorder->DoNotEmitAll(); |
|
279 } else { |
|
280 thisEvent->mEventRule = AccEvent::eDoNotEmit; |
|
281 } |
|
282 |
|
283 return; |
|
284 } |
|
285 |
|
286 thisParent = thisParent->Parent(); |
|
287 } |
|
288 |
|
289 // If tailEvent is contained by thisEvent |
|
290 // then |
|
291 // if show of thisEvent contains the tailEvent |
|
292 // then ignore tailEvent |
|
293 // if hide of thisEvent contains the tailEvent |
|
294 // then assert |
|
295 // otherwise ignore tailEvent but not its show and hide events |
|
296 Accessible* tailParent = aTailEvent->mAccessible; |
|
297 while (tailParent && tailParent != mDocument) { |
|
298 if (tailParent->Parent() == thisEvent->mAccessible) { |
|
299 AccReorderEvent* thisReorder = downcast_accEvent(thisEvent); |
|
300 AccReorderEvent* tailReorder = downcast_accEvent(aTailEvent); |
|
301 uint32_t eventType = thisReorder->IsShowHideEventTarget(tailParent); |
|
302 if (eventType == nsIAccessibleEvent::EVENT_SHOW) |
|
303 tailReorder->DoNotEmitAll(); |
|
304 else if (eventType == nsIAccessibleEvent::EVENT_HIDE) |
|
305 NS_ERROR("Accessible tree was modified after it was removed! Huh?"); |
|
306 else |
|
307 aTailEvent->mEventRule = AccEvent::eDoNotEmit; |
|
308 |
|
309 return; |
|
310 } |
|
311 |
|
312 tailParent = tailParent->Parent(); |
|
313 } |
|
314 |
|
315 } // for (index) |
|
316 } |
|
317 |
|
318 void |
|
319 EventQueue::CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent, |
|
320 AccSelChangeEvent* aThisEvent, |
|
321 uint32_t aThisIndex) |
|
322 { |
|
323 aTailEvent->mPreceedingCount = aThisEvent->mPreceedingCount + 1; |
|
324 |
|
325 // Pack all preceding events into single selection within event |
|
326 // when we receive too much selection add/remove events. |
|
327 if (aTailEvent->mPreceedingCount >= kSelChangeCountToPack) { |
|
328 aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_WITHIN; |
|
329 aTailEvent->mAccessible = aTailEvent->mWidget; |
|
330 aThisEvent->mEventRule = AccEvent::eDoNotEmit; |
|
331 |
|
332 // Do not emit any preceding selection events for same widget if they |
|
333 // weren't coalesced yet. |
|
334 if (aThisEvent->mEventType != nsIAccessibleEvent::EVENT_SELECTION_WITHIN) { |
|
335 for (uint32_t jdx = aThisIndex - 1; jdx < aThisIndex; jdx--) { |
|
336 AccEvent* prevEvent = mEvents[jdx]; |
|
337 if (prevEvent->mEventRule == aTailEvent->mEventRule) { |
|
338 AccSelChangeEvent* prevSelChangeEvent = |
|
339 downcast_accEvent(prevEvent); |
|
340 if (prevSelChangeEvent->mWidget == aTailEvent->mWidget) |
|
341 prevSelChangeEvent->mEventRule = AccEvent::eDoNotEmit; |
|
342 } |
|
343 } |
|
344 } |
|
345 return; |
|
346 } |
|
347 |
|
348 // Pack sequential selection remove and selection add events into |
|
349 // single selection change event. |
|
350 if (aTailEvent->mPreceedingCount == 1 && |
|
351 aTailEvent->mItem != aThisEvent->mItem) { |
|
352 if (aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd && |
|
353 aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) { |
|
354 aThisEvent->mEventRule = AccEvent::eDoNotEmit; |
|
355 aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION; |
|
356 aTailEvent->mPackedEvent = aThisEvent; |
|
357 return; |
|
358 } |
|
359 |
|
360 if (aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd && |
|
361 aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) { |
|
362 aTailEvent->mEventRule = AccEvent::eDoNotEmit; |
|
363 aThisEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION; |
|
364 aThisEvent->mPackedEvent = aTailEvent; |
|
365 return; |
|
366 } |
|
367 } |
|
368 |
|
369 // Unpack the packed selection change event because we've got one |
|
370 // more selection add/remove. |
|
371 if (aThisEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION) { |
|
372 if (aThisEvent->mPackedEvent) { |
|
373 aThisEvent->mPackedEvent->mEventType = |
|
374 aThisEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ? |
|
375 nsIAccessibleEvent::EVENT_SELECTION_ADD : |
|
376 nsIAccessibleEvent::EVENT_SELECTION_REMOVE; |
|
377 |
|
378 aThisEvent->mPackedEvent->mEventRule = |
|
379 AccEvent::eCoalesceSelectionChange; |
|
380 |
|
381 aThisEvent->mPackedEvent = nullptr; |
|
382 } |
|
383 |
|
384 aThisEvent->mEventType = |
|
385 aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ? |
|
386 nsIAccessibleEvent::EVENT_SELECTION_ADD : |
|
387 nsIAccessibleEvent::EVENT_SELECTION_REMOVE; |
|
388 |
|
389 return; |
|
390 } |
|
391 |
|
392 // Convert into selection add since control has single selection but other |
|
393 // selection events for this control are queued. |
|
394 if (aTailEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION) |
|
395 aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD; |
|
396 } |
|
397 |
|
398 void |
|
399 EventQueue::CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent, |
|
400 AccHideEvent* aThisEvent) |
|
401 { |
|
402 // XXX: we need a way to ignore SplitNode and JoinNode() when they do not |
|
403 // affect the text within the hypertext. |
|
404 |
|
405 AccTextChangeEvent* textEvent = aThisEvent->mTextChangeEvent; |
|
406 if (!textEvent) |
|
407 return; |
|
408 |
|
409 if (aThisEvent->mNextSibling == aTailEvent->mAccessible) { |
|
410 aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText); |
|
411 |
|
412 } else if (aThisEvent->mPrevSibling == aTailEvent->mAccessible) { |
|
413 uint32_t oldLen = textEvent->GetLength(); |
|
414 aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText); |
|
415 textEvent->mStart -= textEvent->GetLength() - oldLen; |
|
416 } |
|
417 |
|
418 aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent); |
|
419 } |
|
420 |
|
421 void |
|
422 EventQueue::CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent, |
|
423 AccShowEvent* aThisEvent) |
|
424 { |
|
425 AccTextChangeEvent* textEvent = aThisEvent->mTextChangeEvent; |
|
426 if (!textEvent) |
|
427 return; |
|
428 |
|
429 if (aTailEvent->mAccessible->IndexInParent() == |
|
430 aThisEvent->mAccessible->IndexInParent() + 1) { |
|
431 // If tail target was inserted after this target, i.e. tail target is next |
|
432 // sibling of this target. |
|
433 aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText); |
|
434 |
|
435 } else if (aTailEvent->mAccessible->IndexInParent() == |
|
436 aThisEvent->mAccessible->IndexInParent() -1) { |
|
437 // If tail target was inserted before this target, i.e. tail target is |
|
438 // previous sibling of this target. |
|
439 nsAutoString startText; |
|
440 aTailEvent->mAccessible->AppendTextTo(startText); |
|
441 textEvent->mModifiedText = startText + textEvent->mModifiedText; |
|
442 textEvent->mStart -= startText.Length(); |
|
443 } |
|
444 |
|
445 aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent); |
|
446 } |
|
447 |
|
448 void |
|
449 EventQueue::CreateTextChangeEventFor(AccMutationEvent* aEvent) |
|
450 { |
|
451 Accessible* container = aEvent->mAccessible->Parent(); |
|
452 if (!container) |
|
453 return; |
|
454 |
|
455 HyperTextAccessible* textAccessible = container->AsHyperText(); |
|
456 if (!textAccessible) |
|
457 return; |
|
458 |
|
459 // Don't fire event for the first html:br in an editor. |
|
460 if (aEvent->mAccessible->Role() == roles::WHITESPACE) { |
|
461 nsCOMPtr<nsIEditor> editor = textAccessible->GetEditor(); |
|
462 if (editor) { |
|
463 bool isEmpty = false; |
|
464 editor->GetDocumentIsEmpty(&isEmpty); |
|
465 if (isEmpty) |
|
466 return; |
|
467 } |
|
468 } |
|
469 |
|
470 int32_t offset = textAccessible->GetChildOffset(aEvent->mAccessible); |
|
471 |
|
472 nsAutoString text; |
|
473 aEvent->mAccessible->AppendTextTo(text); |
|
474 if (text.IsEmpty()) |
|
475 return; |
|
476 |
|
477 aEvent->mTextChangeEvent = |
|
478 new AccTextChangeEvent(textAccessible, offset, text, aEvent->IsShow(), |
|
479 aEvent->mIsFromUserInput ? eFromUserInput : eNoUserInput); |
|
480 } |
|
481 |
|
482 //////////////////////////////////////////////////////////////////////////////// |
|
483 // EventQueue: event queue |
|
484 |
|
485 void |
|
486 EventQueue::ProcessEventQueue() |
|
487 { |
|
488 // Process only currently queued events. |
|
489 nsTArray<nsRefPtr<AccEvent> > events; |
|
490 events.SwapElements(mEvents); |
|
491 |
|
492 uint32_t eventCount = events.Length(); |
|
493 #ifdef A11Y_LOG |
|
494 if (eventCount > 0 && logging::IsEnabled(logging::eEvents)) { |
|
495 logging::MsgBegin("EVENTS", "events processing"); |
|
496 logging::Address("document", mDocument); |
|
497 logging::MsgEnd(); |
|
498 } |
|
499 #endif |
|
500 |
|
501 for (uint32_t idx = 0; idx < eventCount; idx++) { |
|
502 AccEvent* event = events[idx]; |
|
503 if (event->mEventRule != AccEvent::eDoNotEmit) { |
|
504 Accessible* target = event->GetAccessible(); |
|
505 if (!target || target->IsDefunct()) |
|
506 continue; |
|
507 |
|
508 // Dispatch the focus event if target is still focused. |
|
509 if (event->mEventType == nsIAccessibleEvent::EVENT_FOCUS) { |
|
510 FocusMgr()->ProcessFocusEvent(event); |
|
511 continue; |
|
512 } |
|
513 |
|
514 // Dispatch caret moved and text selection change events. |
|
515 if (event->mEventType == nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED) { |
|
516 SelectionMgr()->ProcessTextSelChangeEvent(event); |
|
517 continue; |
|
518 } |
|
519 |
|
520 // Fire selected state change events in support to selection events. |
|
521 if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_ADD) { |
|
522 nsEventShell::FireEvent(event->mAccessible, states::SELECTED, |
|
523 true, event->mIsFromUserInput); |
|
524 |
|
525 } else if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_REMOVE) { |
|
526 nsEventShell::FireEvent(event->mAccessible, states::SELECTED, |
|
527 false, event->mIsFromUserInput); |
|
528 |
|
529 } else if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION) { |
|
530 AccSelChangeEvent* selChangeEvent = downcast_accEvent(event); |
|
531 nsEventShell::FireEvent(event->mAccessible, states::SELECTED, |
|
532 (selChangeEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd), |
|
533 event->mIsFromUserInput); |
|
534 |
|
535 if (selChangeEvent->mPackedEvent) { |
|
536 nsEventShell::FireEvent(selChangeEvent->mPackedEvent->mAccessible, |
|
537 states::SELECTED, |
|
538 (selChangeEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd), |
|
539 selChangeEvent->mPackedEvent->mIsFromUserInput); |
|
540 } |
|
541 } |
|
542 |
|
543 nsEventShell::FireEvent(event); |
|
544 |
|
545 // Fire text change events. |
|
546 AccMutationEvent* mutationEvent = downcast_accEvent(event); |
|
547 if (mutationEvent) { |
|
548 if (mutationEvent->mTextChangeEvent) |
|
549 nsEventShell::FireEvent(mutationEvent->mTextChangeEvent); |
|
550 } |
|
551 } |
|
552 |
|
553 if (event->mEventType == nsIAccessibleEvent::EVENT_HIDE) |
|
554 mDocument->ShutdownChildrenInSubtree(event->mAccessible); |
|
555 |
|
556 if (!mDocument) |
|
557 return; |
|
558 } |
|
559 } |