Coverage Report

Created: 2018-09-25 14:53

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