Line data Source code
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 "EventTree.h"
7 :
8 : #include "Accessible-inl.h"
9 : #include "nsEventShell.h"
10 : #include "DocAccessible.h"
11 : #ifdef A11Y_LOG
12 : #include "Logging.h"
13 : #endif
14 :
15 : #include "mozilla/UniquePtr.h"
16 :
17 : using namespace mozilla;
18 : using namespace mozilla::a11y;
19 :
20 : ////////////////////////////////////////////////////////////////////////////////
21 : // TreeMutation class
22 :
23 : EventTree* const TreeMutation::kNoEventTree = reinterpret_cast<EventTree*>(-1);
24 :
25 0 : TreeMutation::TreeMutation(Accessible* aParent, bool aNoEvents) :
26 : mParent(aParent), mStartIdx(UINT32_MAX),
27 0 : mStateFlagsCopy(mParent->mStateFlags),
28 0 : mQueueEvents(!aNoEvents)
29 : {
30 : #ifdef DEBUG
31 0 : mIsDone = false;
32 : #endif
33 :
34 : #ifdef A11Y_LOG
35 0 : if (mQueueEvents && logging::IsEnabled(logging::eEventTree)) {
36 0 : logging::MsgBegin("EVENTS_TREE", "reordering tree before");
37 0 : logging::AccessibleInfo("reordering for", mParent);
38 0 : Controller()->RootEventTree().Log();
39 0 : logging::MsgEnd();
40 :
41 0 : if (logging::IsEnabled(logging::eVerbose)) {
42 0 : logging::Tree("EVENTS_TREE", "Container tree", mParent->Document(),
43 0 : PrefixLog, static_cast<void*>(this));
44 : }
45 : }
46 : #endif
47 :
48 0 : mParent->mStateFlags |= Accessible::eKidsMutating;
49 0 : }
50 :
51 0 : TreeMutation::~TreeMutation()
52 : {
53 0 : MOZ_ASSERT(mIsDone, "Done() must be called explicitly");
54 0 : }
55 :
56 : void
57 0 : TreeMutation::AfterInsertion(Accessible* aChild)
58 : {
59 0 : MOZ_ASSERT(aChild->Parent() == mParent);
60 :
61 0 : if (static_cast<uint32_t>(aChild->mIndexInParent) < mStartIdx) {
62 0 : mStartIdx = aChild->mIndexInParent + 1;
63 : }
64 :
65 0 : if (!mQueueEvents) {
66 0 : return;
67 : }
68 :
69 0 : RefPtr<AccShowEvent> ev = new AccShowEvent(aChild);
70 0 : DebugOnly<bool> added = Controller()->QueueMutationEvent(ev);
71 0 : MOZ_ASSERT(added);
72 0 : aChild->SetShowEventTarget(true);
73 : }
74 :
75 : void
76 0 : TreeMutation::BeforeRemoval(Accessible* aChild, bool aNoShutdown)
77 : {
78 0 : MOZ_ASSERT(aChild->Parent() == mParent);
79 :
80 0 : if (static_cast<uint32_t>(aChild->mIndexInParent) < mStartIdx) {
81 0 : mStartIdx = aChild->mIndexInParent;
82 : }
83 :
84 0 : if (!mQueueEvents) {
85 0 : return;
86 : }
87 :
88 0 : RefPtr<AccHideEvent> ev = new AccHideEvent(aChild, !aNoShutdown);
89 0 : if (Controller()->QueueMutationEvent(ev)) {
90 0 : aChild->SetHideEventTarget(true);
91 : }
92 : }
93 :
94 : void
95 0 : TreeMutation::Done()
96 : {
97 0 : MOZ_ASSERT(mParent->mStateFlags & Accessible::eKidsMutating);
98 0 : mParent->mStateFlags &= ~Accessible::eKidsMutating;
99 :
100 0 : uint32_t length = mParent->mChildren.Length();
101 : #ifdef DEBUG
102 0 : for (uint32_t idx = 0; idx < mStartIdx && idx < length; idx++) {
103 0 : MOZ_ASSERT(mParent->mChildren[idx]->mIndexInParent == static_cast<int32_t>(idx),
104 : "Wrong index detected");
105 : }
106 : #endif
107 :
108 0 : for (uint32_t idx = mStartIdx; idx < length; idx++) {
109 0 : mParent->mChildren[idx]->mInt.mIndexOfEmbeddedChild = -1;
110 0 : mParent->mChildren[idx]->mStateFlags |= Accessible::eGroupInfoDirty;
111 : }
112 :
113 0 : mParent->mEmbeddedObjCollector = nullptr;
114 0 : mParent->mStateFlags |= mStateFlagsCopy & Accessible::eKidsMutating;
115 :
116 : #ifdef DEBUG
117 0 : mIsDone = true;
118 : #endif
119 :
120 : #ifdef A11Y_LOG
121 0 : if (mQueueEvents && logging::IsEnabled(logging::eEventTree)) {
122 0 : logging::MsgBegin("EVENTS_TREE", "reordering tree after");
123 0 : logging::AccessibleInfo("reordering for", mParent);
124 0 : Controller()->RootEventTree().Log();
125 0 : logging::MsgEnd();
126 : }
127 : #endif
128 0 : }
129 :
130 : #ifdef A11Y_LOG
131 : const char*
132 0 : TreeMutation::PrefixLog(void* aData, Accessible* aAcc)
133 : {
134 0 : TreeMutation* thisObj = reinterpret_cast<TreeMutation*>(aData);
135 0 : if (thisObj->mParent == aAcc) {
136 0 : return "_X_";
137 : }
138 0 : const EventTree& ret = thisObj->Controller()->RootEventTree();
139 0 : if (ret.Find(aAcc)) {
140 0 : return "_с_";
141 : }
142 0 : return "";
143 : }
144 : #endif
145 :
146 :
147 : ////////////////////////////////////////////////////////////////////////////////
148 : // EventTree
149 :
150 : void
151 0 : EventTree::Shown(Accessible* aChild)
152 : {
153 0 : RefPtr<AccShowEvent> ev = new AccShowEvent(aChild);
154 0 : Controller(aChild)->WithdrawPrecedingEvents(&ev->mPrecedingEvents);
155 0 : Mutated(ev);
156 0 : }
157 :
158 : void
159 0 : EventTree::Hidden(Accessible* aChild, bool aNeedsShutdown)
160 : {
161 0 : RefPtr<AccHideEvent> ev = new AccHideEvent(aChild, aNeedsShutdown);
162 0 : if (!aNeedsShutdown) {
163 0 : Controller(aChild)->StorePrecedingEvent(ev);
164 : }
165 0 : Mutated(ev);
166 0 : }
167 :
168 : void
169 0 : EventTree::Process(const RefPtr<DocAccessible>& aDeathGrip)
170 : {
171 0 : while (mFirst) {
172 : // Skip a node and its subtree if its container is not in the document.
173 0 : if (mFirst->mContainer->IsInDocument()) {
174 0 : mFirst->Process(aDeathGrip);
175 0 : if (aDeathGrip->IsDefunct()) {
176 0 : return;
177 : }
178 : }
179 0 : mFirst = Move(mFirst->mNext);
180 : }
181 :
182 0 : MOZ_ASSERT(mContainer || mDependentEvents.IsEmpty(),
183 : "No container, no events");
184 0 : MOZ_ASSERT(!mContainer || !mContainer->IsDefunct(),
185 : "Processing events for defunct container");
186 0 : MOZ_ASSERT(!mFireReorder || mContainer, "No target for reorder event");
187 :
188 : // Fire mutation events.
189 0 : uint32_t eventsCount = mDependentEvents.Length();
190 0 : for (uint32_t jdx = 0; jdx < eventsCount; jdx++) {
191 0 : AccMutationEvent* mtEvent = mDependentEvents[jdx];
192 0 : MOZ_ASSERT(mtEvent->Document(), "No document for event target");
193 :
194 : // Fire all hide events that has to be fired before this show event.
195 0 : if (mtEvent->IsShow()) {
196 0 : AccShowEvent* showEv = downcast_accEvent(mtEvent);
197 0 : for (uint32_t i = 0; i < showEv->mPrecedingEvents.Length(); i++) {
198 0 : nsEventShell::FireEvent(showEv->mPrecedingEvents[i]);
199 0 : if (aDeathGrip->IsDefunct()) {
200 0 : return;
201 : }
202 : }
203 : }
204 :
205 0 : nsEventShell::FireEvent(mtEvent);
206 0 : if (aDeathGrip->IsDefunct()) {
207 0 : return;
208 : }
209 :
210 0 : if (mtEvent->mTextChangeEvent) {
211 0 : nsEventShell::FireEvent(mtEvent->mTextChangeEvent);
212 0 : if (aDeathGrip->IsDefunct()) {
213 0 : return;
214 : }
215 : }
216 :
217 0 : if (mtEvent->IsHide()) {
218 : // Fire menupopup end event before a hide event if a menu goes away.
219 :
220 : // XXX: We don't look into children of hidden subtree to find hiding
221 : // menupopup (as we did prior bug 570275) because we don't do that when
222 : // menu is showing (and that's impossible until bug 606924 is fixed).
223 : // Nevertheless we should do this at least because layout coalesces
224 : // the changes before our processing and we may miss some menupopup
225 : // events. Now we just want to be consistent in content insertion/removal
226 : // handling.
227 0 : if (mtEvent->mAccessible->ARIARole() == roles::MENUPOPUP) {
228 0 : nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
229 0 : mtEvent->mAccessible);
230 0 : if (aDeathGrip->IsDefunct()) {
231 0 : return;
232 : }
233 : }
234 :
235 0 : AccHideEvent* hideEvent = downcast_accEvent(mtEvent);
236 0 : if (hideEvent->NeedsShutdown()) {
237 0 : aDeathGrip->ShutdownChildrenInSubtree(mtEvent->mAccessible);
238 : }
239 : }
240 : }
241 :
242 : // Fire reorder event at last.
243 0 : if (mFireReorder) {
244 0 : nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_REORDER, mContainer);
245 0 : mContainer->Document()->MaybeNotifyOfValueChange(mContainer);
246 : }
247 :
248 0 : mDependentEvents.Clear();
249 : }
250 :
251 : EventTree*
252 0 : EventTree::FindOrInsert(Accessible* aContainer)
253 : {
254 0 : if (!mFirst) {
255 0 : mFirst.reset(new EventTree(aContainer, mDependentEvents.IsEmpty()));
256 0 : return mFirst.get();
257 : }
258 :
259 0 : EventTree* prevNode = nullptr;
260 0 : EventTree* node = mFirst.get();
261 0 : do {
262 0 : MOZ_ASSERT(!node->mContainer->IsApplication(),
263 : "No event for application accessible is expected here");
264 0 : MOZ_ASSERT(!node->mContainer->IsDefunct(), "An event target has to be alive");
265 :
266 : // Case of same target.
267 0 : if (node->mContainer == aContainer) {
268 0 : return node;
269 : }
270 :
271 : // Check if the given container is contained by a current node
272 0 : Accessible* top = mContainer ? mContainer : aContainer->Document();
273 0 : Accessible* parent = aContainer;
274 0 : while (parent) {
275 : // Reached a top, no match for a current event.
276 0 : if (parent == top) {
277 0 : break;
278 : }
279 :
280 : // We got a match.
281 0 : if (parent->Parent() == node->mContainer) {
282 : // Reject the node if it's contained by a show/hide event target
283 0 : uint32_t evCount = node->mDependentEvents.Length();
284 0 : for (uint32_t idx = 0; idx < evCount; idx++) {
285 0 : AccMutationEvent* ev = node->mDependentEvents[idx];
286 0 : if (ev->GetAccessible() == parent) {
287 : #ifdef A11Y_LOG
288 0 : if (logging::IsEnabled(logging::eEventTree)) {
289 : logging::MsgBegin("EVENTS_TREE",
290 0 : "Rejecting node contained by show/hide");
291 0 : logging::AccessibleInfo("Node", aContainer);
292 0 : logging::MsgEnd();
293 : }
294 : #endif
295 : // If the node is rejected, then check if it has related hide event
296 : // on stack, and if so, then connect it to the parent show event.
297 0 : if (ev->IsShow()) {
298 0 : AccShowEvent* showEv = downcast_accEvent(ev);
299 : Controller(aContainer)->
300 0 : WithdrawPrecedingEvents(&showEv->mPrecedingEvents);
301 : }
302 0 : return nullptr;
303 : }
304 : }
305 :
306 0 : return node->FindOrInsert(aContainer);
307 : }
308 :
309 0 : parent = parent->Parent();
310 0 : MOZ_ASSERT(parent, "Wrong tree");
311 : }
312 :
313 : // If the given container contains a current node
314 : // then
315 : // if show or hide of the given node contains a grand parent of the current node
316 : // then ignore the current node and its show and hide events
317 : // otherwise ignore the current node, but not its show and hide events
318 0 : Accessible* curParent = node->mContainer;
319 0 : while (curParent && !curParent->IsDoc()) {
320 0 : if (curParent->Parent() != aContainer) {
321 0 : curParent = curParent->Parent();
322 0 : continue;
323 : }
324 :
325 : // Insert the tail node into the hierarchy between the current node and
326 : // its parent.
327 0 : node->mFireReorder = false;
328 0 : UniquePtr<EventTree>& nodeOwnerRef = prevNode ? prevNode->mNext : mFirst;
329 0 : UniquePtr<EventTree> newNode(new EventTree(aContainer, mDependentEvents.IsEmpty()));
330 0 : newNode->mFirst = Move(nodeOwnerRef);
331 0 : nodeOwnerRef = Move(newNode);
332 0 : nodeOwnerRef->mNext = Move(node->mNext);
333 :
334 : // Check if a next node is contained by the given node too, and move them
335 : // under the given node if so.
336 0 : prevNode = nodeOwnerRef.get();
337 0 : node = nodeOwnerRef->mNext.get();
338 0 : UniquePtr<EventTree>* nodeRef = &nodeOwnerRef->mNext;
339 0 : EventTree* insNode = nodeOwnerRef->mFirst.get();
340 0 : while (node) {
341 0 : Accessible* curParent = node->mContainer;
342 0 : while (curParent && !curParent->IsDoc()) {
343 0 : if (curParent->Parent() != aContainer) {
344 0 : curParent = curParent->Parent();
345 0 : continue;
346 : }
347 :
348 0 : MOZ_ASSERT(!insNode->mNext);
349 :
350 0 : node->mFireReorder = false;
351 0 : insNode->mNext = Move(*nodeRef);
352 0 : insNode = insNode->mNext.get();
353 :
354 0 : prevNode->mNext = Move(node->mNext);
355 0 : node = prevNode;
356 0 : break;
357 : }
358 :
359 0 : prevNode = node;
360 0 : nodeRef = &node->mNext;
361 0 : node = node->mNext.get();
362 : }
363 :
364 0 : return nodeOwnerRef.get();
365 : }
366 :
367 0 : prevNode = node;
368 0 : } while ((node = node->mNext.get()));
369 :
370 0 : MOZ_ASSERT(prevNode, "Nowhere to insert");
371 0 : MOZ_ASSERT(!prevNode->mNext, "Taken by another node");
372 :
373 : // If 'this' node contains the given container accessible, then
374 : // do not emit a reorder event for the container
375 : // if a dependent show event target contains the given container then do not
376 : // emit show / hide events (see Process() method)
377 :
378 0 : prevNode->mNext.reset(new EventTree(aContainer, mDependentEvents.IsEmpty()));
379 0 : return prevNode->mNext.get();
380 : }
381 :
382 : void
383 0 : EventTree::Clear()
384 : {
385 0 : mFirst = nullptr;
386 0 : mNext = nullptr;
387 0 : mContainer = nullptr;
388 :
389 0 : uint32_t eventsCount = mDependentEvents.Length();
390 0 : for (uint32_t jdx = 0; jdx < eventsCount; jdx++) {
391 0 : mDependentEvents[jdx]->mEventType = AccEvent::eDoNotEmit;
392 0 : AccHideEvent* ev = downcast_accEvent(mDependentEvents[jdx]);
393 0 : if (ev && ev->NeedsShutdown()) {
394 0 : ev->Document()->ShutdownChildrenInSubtree(ev->mAccessible);
395 : }
396 : }
397 0 : mDependentEvents.Clear();
398 0 : }
399 :
400 : const EventTree*
401 0 : EventTree::Find(const Accessible* aContainer) const
402 : {
403 0 : const EventTree* et = this;
404 0 : while (et) {
405 0 : if (et->mContainer == aContainer) {
406 0 : return et;
407 : }
408 :
409 0 : if (et->mFirst) {
410 0 : et = et->mFirst.get();
411 0 : const EventTree* cet = et->Find(aContainer);
412 0 : if (cet) {
413 0 : return cet;
414 : }
415 : }
416 :
417 0 : et = et->mNext.get();
418 0 : const EventTree* cet = et->Find(aContainer);
419 0 : if (cet) {
420 0 : return cet;
421 : }
422 : }
423 :
424 0 : return nullptr;
425 : }
426 :
427 : #ifdef A11Y_LOG
428 : void
429 0 : EventTree::Log(uint32_t aLevel) const
430 : {
431 0 : if (aLevel == UINT32_MAX) {
432 0 : if (mFirst) {
433 0 : mFirst->Log(0);
434 : }
435 0 : return;
436 : }
437 :
438 0 : for (uint32_t i = 0; i < aLevel; i++) {
439 0 : printf(" ");
440 : }
441 0 : logging::AccessibleInfo("container", mContainer);
442 :
443 0 : for (uint32_t i = 0; i < mDependentEvents.Length(); i++) {
444 0 : AccMutationEvent* ev = mDependentEvents[i];
445 0 : if (ev->IsShow()) {
446 0 : for (uint32_t i = 0; i < aLevel + 1; i++) {
447 0 : printf(" ");
448 : }
449 0 : logging::AccessibleInfo("shown", ev->mAccessible);
450 :
451 0 : AccShowEvent* showEv = downcast_accEvent(ev);
452 0 : for (uint32_t i = 0; i < showEv->mPrecedingEvents.Length(); i++) {
453 0 : for (uint32_t j = 0; j < aLevel + 1; j++) {
454 0 : printf(" ");
455 : }
456 0 : logging::AccessibleInfo("preceding",
457 0 : showEv->mPrecedingEvents[i]->mAccessible);
458 : }
459 : }
460 : else {
461 0 : for (uint32_t i = 0; i < aLevel + 1; i++) {
462 0 : printf(" ");
463 : }
464 0 : logging::AccessibleInfo("hidden", ev->mAccessible);
465 : }
466 : }
467 :
468 0 : if (mFirst) {
469 0 : mFirst->Log(aLevel + 1);
470 : }
471 :
472 0 : if (mNext) {
473 0 : mNext->Log(aLevel);
474 : }
475 : }
476 : #endif
477 :
478 : void
479 0 : EventTree::Mutated(AccMutationEvent* aEv)
480 : {
481 : // If shown or hidden node is a root of previously mutated subtree, then
482 : // discard those subtree mutations as we are no longer interested in them.
483 0 : UniquePtr<EventTree>* node = &mFirst;
484 0 : while (*node) {
485 0 : Accessible* cntr = (*node)->mContainer;
486 0 : while (cntr != mContainer) {
487 0 : if (cntr == aEv->mAccessible) {
488 : #ifdef A11Y_LOG
489 0 : if (logging::IsEnabled(logging::eEventTree)) {
490 0 : logging::MsgBegin("EVENTS_TREE", "Trim subtree");
491 0 : logging::AccessibleInfo("Show/hide container", aEv->mAccessible);
492 0 : logging::AccessibleInfo("Trimmed subtree root", (*node)->mContainer);
493 0 : logging::MsgEnd();
494 : }
495 : #endif
496 :
497 : // If the new hide is part of a move and it contains existing child
498 : // shows, then move preceding events from the child shows to the buffer,
499 : // so the ongoing show event will pick them up.
500 0 : if (aEv->IsHide()) {
501 0 : AccHideEvent* hideEv = downcast_accEvent(aEv);
502 0 : if (!hideEv->mNeedsShutdown) {
503 0 : for (uint32_t i = 0; i < (*node)->mDependentEvents.Length(); i++) {
504 0 : AccMutationEvent* childEv = (*node)->mDependentEvents[i];
505 0 : if (childEv->IsShow()) {
506 0 : AccShowEvent* childShowEv = downcast_accEvent(childEv);
507 0 : if (childShowEv->mPrecedingEvents.Length() > 0) {
508 0 : Controller(mContainer)->StorePrecedingEvents(
509 0 : mozilla::Move(childShowEv->mPrecedingEvents));
510 : }
511 : }
512 : }
513 : }
514 : }
515 : // If the new show contains existing child shows, then move preceding
516 : // events from the child shows to the new show.
517 0 : else if (aEv->IsShow()) {
518 0 : AccShowEvent* showEv = downcast_accEvent(aEv);
519 0 : for (uint32_t i = 0; (*node)->mDependentEvents.Length(); i++) {
520 0 : AccMutationEvent* childEv = (*node)->mDependentEvents[i];
521 0 : if (childEv->IsShow()) {
522 0 : AccShowEvent* showChildEv = downcast_accEvent(childEv);
523 0 : if (showChildEv->mPrecedingEvents.Length() > 0) {
524 : #ifdef A11Y_LOG
525 0 : if (logging::IsEnabled(logging::eEventTree)) {
526 0 : logging::MsgBegin("EVENTS_TREE", "Adopt preceding events");
527 0 : logging::AccessibleInfo("Parent", aEv->mAccessible);
528 0 : for (uint32_t j = 0; j < showChildEv->mPrecedingEvents.Length(); j++) {
529 0 : logging::AccessibleInfo("Adoptee",
530 0 : showChildEv->mPrecedingEvents[i]->mAccessible);
531 : }
532 0 : logging::MsgEnd();
533 : }
534 : #endif
535 0 : showEv->mPrecedingEvents.AppendElements(showChildEv->mPrecedingEvents);
536 : }
537 : }
538 : }
539 : }
540 :
541 0 : *node = Move((*node)->mNext);
542 0 : break;
543 : }
544 0 : cntr = cntr->Parent();
545 : }
546 0 : if (cntr == aEv->mAccessible) {
547 0 : continue;
548 : }
549 0 : node = &(*node)->mNext;
550 : }
551 :
552 0 : AccMutationEvent* prevEvent = mDependentEvents.SafeLastElement(nullptr);
553 0 : mDependentEvents.AppendElement(aEv);
554 :
555 : // Coalesce text change events from this hide/show event and the previous one.
556 0 : if (prevEvent && aEv->mEventType == prevEvent->mEventType) {
557 0 : if (aEv->IsHide()) {
558 : // XXX: we need a way to ignore SplitNode and JoinNode() when they do not
559 : // affect the text within the hypertext.
560 0 : AccTextChangeEvent* prevTextEvent = prevEvent->mTextChangeEvent;
561 0 : if (prevTextEvent) {
562 0 : AccHideEvent* hideEvent = downcast_accEvent(aEv);
563 0 : AccHideEvent* prevHideEvent = downcast_accEvent(prevEvent);
564 :
565 0 : if (prevHideEvent->mNextSibling == hideEvent->mAccessible) {
566 0 : hideEvent->mAccessible->AppendTextTo(prevTextEvent->mModifiedText);
567 : }
568 0 : else if (prevHideEvent->mPrevSibling == hideEvent->mAccessible) {
569 0 : uint32_t oldLen = prevTextEvent->GetLength();
570 0 : hideEvent->mAccessible->AppendTextTo(prevTextEvent->mModifiedText);
571 0 : prevTextEvent->mStart -= prevTextEvent->GetLength() - oldLen;
572 : }
573 :
574 0 : hideEvent->mTextChangeEvent.swap(prevEvent->mTextChangeEvent);
575 : }
576 : }
577 : else {
578 0 : AccTextChangeEvent* prevTextEvent = prevEvent->mTextChangeEvent;
579 0 : if (prevTextEvent) {
580 0 : if (aEv->mAccessible->IndexInParent() ==
581 0 : prevEvent->mAccessible->IndexInParent() + 1) {
582 : // If tail target was inserted after this target, i.e. tail target is next
583 : // sibling of this target.
584 0 : aEv->mAccessible->AppendTextTo(prevTextEvent->mModifiedText);
585 : }
586 0 : else if (aEv->mAccessible->IndexInParent() ==
587 0 : prevEvent->mAccessible->IndexInParent() - 1) {
588 : // If tail target was inserted before this target, i.e. tail target is
589 : // previous sibling of this target.
590 0 : nsAutoString startText;
591 0 : aEv->mAccessible->AppendTextTo(startText);
592 0 : prevTextEvent->mModifiedText = startText + prevTextEvent->mModifiedText;
593 0 : prevTextEvent->mStart -= startText.Length();
594 : }
595 :
596 0 : aEv->mTextChangeEvent.swap(prevEvent->mTextChangeEvent);
597 : }
598 : }
599 : }
600 :
601 : // Create a text change event caused by this hide/show event. When a node is
602 : // hidden/removed or shown/appended, the text in an ancestor hyper text will
603 : // lose or get new characters.
604 0 : if (aEv->mTextChangeEvent || !mContainer->IsHyperText()) {
605 0 : return;
606 : }
607 :
608 0 : nsAutoString text;
609 0 : aEv->mAccessible->AppendTextTo(text);
610 0 : if (text.IsEmpty()) {
611 0 : return;
612 : }
613 :
614 0 : int32_t offset = mContainer->AsHyperText()->GetChildOffset(aEv->mAccessible);
615 0 : aEv->mTextChangeEvent =
616 0 : new AccTextChangeEvent(mContainer, offset, text, aEv->IsShow(),
617 0 : aEv->mIsFromUserInput ? eFromUserInput : eNoUserInput);
618 : }
|