Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/accessible/ipc/DocAccessibleParent.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "DocAccessibleParent.h"
8
#include "mozilla/a11y/Platform.h"
9
#include "mozilla/dom/TabParent.h"
10
#include "xpcAccessibleDocument.h"
11
#include "xpcAccEvents.h"
12
#include "nsAccUtils.h"
13
#include "nsCoreUtils.h"
14
15
#if defined(XP_WIN)
16
#include "AccessibleWrap.h"
17
#include "Compatibility.h"
18
#include "mozilla/mscom/PassthruProxy.h"
19
#include "mozilla/mscom/Ptr.h"
20
#include "nsWinUtils.h"
21
#include "RootAccessible.h"
22
#endif
23
24
namespace mozilla {
25
namespace a11y {
26
uint64_t DocAccessibleParent::sMaxDocID = 0;
27
28
mozilla::ipc::IPCResult
29
DocAccessibleParent::RecvShowEvent(const ShowEventData& aData,
30
                                   const bool& aFromUser)
31
0
{
32
0
  if (mShutdown)
33
0
    return IPC_OK();
34
0
35
0
  MOZ_ASSERT(CheckDocTree());
36
0
37
0
  if (aData.NewTree().IsEmpty()) {
38
0
    return IPC_FAIL(this, "No children being added");
39
0
  }
40
0
41
0
  ProxyAccessible* parent = GetAccessible(aData.ID());
42
0
43
0
  // XXX This should really never happen, but sometimes we fail to fire the
44
0
  // required show events.
45
0
  if (!parent) {
46
0
    NS_ERROR("adding child to unknown accessible");
47
#ifdef DEBUG
48
    return IPC_FAIL(this, "unknown parent accessible");
49
#else
50
0
    return IPC_OK();
51
0
#endif
52
0
  }
53
0
54
0
  uint32_t newChildIdx = aData.Idx();
55
0
  if (newChildIdx > parent->ChildrenCount()) {
56
0
    NS_ERROR("invalid index to add child at");
57
#ifdef DEBUG
58
    return IPC_FAIL(this, "invalid index");
59
#else
60
0
    return IPC_OK();
61
0
#endif
62
0
  }
63
0
64
0
  uint32_t consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx);
65
0
  MOZ_ASSERT(consumed == aData.NewTree().Length());
66
0
67
0
  // XXX This shouldn't happen, but if we failed to add children then the below
68
0
  // is pointless and can crash.
69
0
  if (!consumed) {
70
0
    return IPC_FAIL(this, "failed to add children");
71
0
  }
72
0
73
#ifdef DEBUG
74
  for (uint32_t i = 0; i < consumed; i++) {
75
    uint64_t id = aData.NewTree()[i].ID();
76
    MOZ_ASSERT(mAccessibles.GetEntry(id));
77
  }
78
#endif
79
80
0
  MOZ_ASSERT(CheckDocTree());
81
0
82
0
  // Just update, no events.
83
0
  if (aData.EventSuppressed()) {
84
0
    return IPC_OK();
85
0
  }
86
0
87
0
  ProxyAccessible* target = parent->ChildAt(newChildIdx);
88
0
  ProxyShowHideEvent(target, parent, true, aFromUser);
89
0
90
0
  if (!nsCoreUtils::AccEventObserversExist()) {
91
0
    return IPC_OK();
92
0
  }
93
0
94
0
  uint32_t type = nsIAccessibleEvent::EVENT_SHOW;
95
0
  xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
96
0
  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
97
0
  nsINode* node = nullptr;
98
0
  RefPtr<xpcAccEvent> event = new xpcAccEvent(type, xpcAcc, doc, node,
99
0
                                              aFromUser);
100
0
  nsCoreUtils::DispatchAccEvent(std::move(event));
101
0
102
0
  return IPC_OK();
103
0
}
104
105
uint32_t
106
DocAccessibleParent::AddSubtree(ProxyAccessible* aParent,
107
                                const nsTArray<a11y::AccessibleData>& aNewTree,
108
                                uint32_t aIdx, uint32_t aIdxInParent)
109
0
{
110
0
  if (aNewTree.Length() <= aIdx) {
111
0
    NS_ERROR("bad index in serialized tree!");
112
0
    return 0;
113
0
  }
114
0
115
0
  const AccessibleData& newChild = aNewTree[aIdx];
116
0
117
0
  if (mAccessibles.Contains(newChild.ID())) {
118
0
    NS_ERROR("ID already in use");
119
0
    return 0;
120
0
  }
121
0
122
0
  ProxyAccessible* newProxy = new ProxyAccessible(
123
0
    newChild.ID(), aParent, this, newChild.Role(), newChild.Interfaces());
124
0
125
0
  aParent->AddChildAt(aIdxInParent, newProxy);
126
0
  mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy;
127
0
  ProxyCreated(newProxy, newChild.Interfaces());
128
0
129
#if defined(XP_WIN)
130
  WrapperFor(newProxy)->SetID(newChild.MsaaID());
131
#endif
132
133
0
  uint32_t accessibles = 1;
134
0
  uint32_t kids = newChild.ChildrenCount();
135
0
  for (uint32_t i = 0; i < kids; i++) {
136
0
    uint32_t consumed = AddSubtree(newProxy, aNewTree, aIdx + accessibles, i);
137
0
    if (!consumed)
138
0
      return 0;
139
0
140
0
    accessibles += consumed;
141
0
  }
142
0
143
0
  MOZ_ASSERT(newProxy->ChildrenCount() == kids);
144
0
145
0
  return accessibles;
146
0
}
147
148
mozilla::ipc::IPCResult
149
DocAccessibleParent::RecvHideEvent(const uint64_t& aRootID,
150
                                   const bool& aFromUser)
151
0
{
152
0
  if (mShutdown)
153
0
    return IPC_OK();
154
0
155
0
  MOZ_ASSERT(CheckDocTree());
156
0
157
0
  // We shouldn't actually need this because mAccessibles shouldn't have an
158
0
  // entry for the document itself, but it doesn't hurt to be explicit.
159
0
  if (!aRootID) {
160
0
    return IPC_FAIL(this, "Trying to hide entire document?");
161
0
  }
162
0
163
0
  ProxyEntry* rootEntry = mAccessibles.GetEntry(aRootID);
164
0
  if (!rootEntry) {
165
0
    NS_ERROR("invalid root being removed!");
166
0
    return IPC_OK();
167
0
  }
168
0
169
0
  ProxyAccessible* root = rootEntry->mProxy;
170
0
  if (!root) {
171
0
    NS_ERROR("invalid root being removed!");
172
0
    return IPC_OK();
173
0
  }
174
0
175
0
  ProxyAccessible* parent = root->Parent();
176
0
  ProxyShowHideEvent(root, parent, false, aFromUser);
177
0
178
0
  RefPtr<xpcAccHideEvent> event = nullptr;
179
0
  if (nsCoreUtils::AccEventObserversExist()) {
180
0
    uint32_t type = nsIAccessibleEvent::EVENT_HIDE;
181
0
    xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(root);
182
0
    xpcAccessibleGeneric* xpcParent = GetXPCAccessible(parent);
183
0
    ProxyAccessible* next = root->NextSibling();
184
0
    xpcAccessibleGeneric* xpcNext = next ? GetXPCAccessible(next) : nullptr;
185
0
    ProxyAccessible* prev = root->PrevSibling();
186
0
    xpcAccessibleGeneric* xpcPrev = prev ? GetXPCAccessible(prev) : nullptr;
187
0
    xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
188
0
    nsINode* node = nullptr;
189
0
    event = new xpcAccHideEvent(type, xpcAcc, doc, node, aFromUser, xpcParent,
190
0
                                xpcNext, xpcPrev);
191
0
  }
192
0
193
0
  parent->RemoveChild(root);
194
0
  root->Shutdown();
195
0
196
0
  MOZ_ASSERT(CheckDocTree());
197
0
198
0
  if (event) {
199
0
    nsCoreUtils::DispatchAccEvent(std::move(event));
200
0
  }
201
0
202
0
  return IPC_OK();
203
0
}
204
205
mozilla::ipc::IPCResult
206
DocAccessibleParent::RecvEvent(const uint64_t& aID, const uint32_t& aEventType)
207
0
{
208
0
  if (mShutdown) {
209
0
    return IPC_OK();
210
0
  }
211
0
212
0
  ProxyAccessible* proxy = GetAccessible(aID);
213
0
  if (!proxy) {
214
0
    NS_ERROR("no proxy for event!");
215
0
    return IPC_OK();
216
0
  }
217
0
218
0
  ProxyEvent(proxy, aEventType);
219
0
220
0
  if (!nsCoreUtils::AccEventObserversExist()) {
221
0
    return IPC_OK();
222
0
  }
223
0
224
0
  xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
225
0
  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
226
0
  nsINode* node = nullptr;
227
0
  bool fromUser = true; // XXX fix me
228
0
  RefPtr<xpcAccEvent> event = new xpcAccEvent(aEventType, xpcAcc, doc, node,
229
0
                                              fromUser);
230
0
  nsCoreUtils::DispatchAccEvent(std::move(event));
231
0
232
0
  return IPC_OK();
233
0
}
234
235
mozilla::ipc::IPCResult
236
DocAccessibleParent::RecvStateChangeEvent(const uint64_t& aID,
237
                                          const uint64_t& aState,
238
                                          const bool& aEnabled)
239
0
{
240
0
  if (mShutdown) {
241
0
    return IPC_OK();
242
0
  }
243
0
244
0
  ProxyAccessible* target = GetAccessible(aID);
245
0
  if (!target) {
246
0
    NS_ERROR("we don't know about the target of a state change event!");
247
0
    return IPC_OK();
248
0
  }
249
0
250
0
  ProxyStateChangeEvent(target, aState, aEnabled);
251
0
252
0
  if (!nsCoreUtils::AccEventObserversExist()) {
253
0
    return IPC_OK();
254
0
  }
255
0
256
0
  xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
257
0
  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
258
0
  uint32_t type = nsIAccessibleEvent::EVENT_STATE_CHANGE;
259
0
  bool extra;
260
0
  uint32_t state = nsAccUtils::To32States(aState, &extra);
261
0
  bool fromUser = true; // XXX fix this
262
0
  nsINode* node = nullptr; // XXX can we do better?
263
0
  RefPtr<xpcAccStateChangeEvent> event =
264
0
    new xpcAccStateChangeEvent(type, xpcAcc, doc, node, fromUser, state, extra,
265
0
                               aEnabled);
266
0
  nsCoreUtils::DispatchAccEvent(std::move(event));
267
0
268
0
  return IPC_OK();
269
0
}
270
271
mozilla::ipc::IPCResult
272
DocAccessibleParent::RecvCaretMoveEvent(const uint64_t& aID,
273
#if defined(XP_WIN)
274
                                        const LayoutDeviceIntRect& aCaretRect,
275
#endif // defined (XP_WIN)
276
                                        const int32_t& aOffset)
277
0
{
278
0
  if (mShutdown) {
279
0
    return IPC_OK();
280
0
  }
281
0
282
0
  ProxyAccessible* proxy = GetAccessible(aID);
283
0
  if (!proxy) {
284
0
    NS_ERROR("unknown caret move event target!");
285
0
    return IPC_OK();
286
0
  }
287
0
288
#if defined(XP_WIN)
289
  ProxyCaretMoveEvent(proxy, aCaretRect);
290
#else
291
0
  ProxyCaretMoveEvent(proxy, aOffset);
292
0
#endif
293
0
294
0
  if (!nsCoreUtils::AccEventObserversExist()) {
295
0
    return IPC_OK();
296
0
  }
297
0
298
0
  xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
299
0
  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
300
0
  nsINode* node = nullptr;
301
0
  bool fromUser = true; // XXX fix me
302
0
  uint32_t type = nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED;
303
0
  RefPtr<xpcAccCaretMoveEvent> event =
304
0
    new xpcAccCaretMoveEvent(type, xpcAcc, doc, node, fromUser, aOffset);
305
0
  nsCoreUtils::DispatchAccEvent(std::move(event));
306
0
307
0
  return IPC_OK();
308
0
}
309
310
mozilla::ipc::IPCResult
311
DocAccessibleParent::RecvTextChangeEvent(const uint64_t& aID,
312
                                         const nsString& aStr,
313
                                         const int32_t& aStart,
314
                                         const uint32_t& aLen,
315
                                         const bool& aIsInsert,
316
                                         const bool& aFromUser)
317
0
{
318
0
  if (mShutdown) {
319
0
    return IPC_OK();
320
0
  }
321
0
322
0
  ProxyAccessible* target = GetAccessible(aID);
323
0
  if (!target) {
324
0
    NS_ERROR("text change event target is unknown!");
325
0
    return IPC_OK();
326
0
  }
327
0
328
0
  ProxyTextChangeEvent(target, aStr, aStart, aLen, aIsInsert, aFromUser);
329
0
330
0
  if (!nsCoreUtils::AccEventObserversExist()) {
331
0
    return IPC_OK();
332
0
  }
333
0
334
0
  xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
335
0
  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
336
0
  uint32_t type = aIsInsert ? nsIAccessibleEvent::EVENT_TEXT_INSERTED :
337
0
                              nsIAccessibleEvent::EVENT_TEXT_REMOVED;
338
0
  nsINode* node = nullptr;
339
0
  RefPtr<xpcAccTextChangeEvent> event =
340
0
    new xpcAccTextChangeEvent(type, xpcAcc, doc, node, aFromUser, aStart, aLen,
341
0
                              aIsInsert, aStr);
342
0
  nsCoreUtils::DispatchAccEvent(std::move(event));
343
0
344
0
  return IPC_OK();
345
0
}
346
347
#if defined(XP_WIN)
348
349
mozilla::ipc::IPCResult
350
DocAccessibleParent::RecvSyncTextChangeEvent(const uint64_t& aID,
351
                                             const nsString& aStr,
352
                                             const int32_t& aStart,
353
                                             const uint32_t& aLen,
354
                                             const bool& aIsInsert,
355
                                             const bool& aFromUser)
356
{
357
  return RecvTextChangeEvent(aID, aStr, aStart, aLen, aIsInsert, aFromUser);
358
}
359
360
#endif // defined(XP_WIN)
361
362
mozilla::ipc::IPCResult
363
DocAccessibleParent::RecvSelectionEvent(const uint64_t& aID,
364
                                        const uint64_t& aWidgetID,
365
                                        const uint32_t& aType)
366
0
{
367
0
  if (mShutdown) {
368
0
    return IPC_OK();
369
0
  }
370
0
371
0
  ProxyAccessible* target = GetAccessible(aID);
372
0
  ProxyAccessible* widget = GetAccessible(aWidgetID);
373
0
  if (!target || !widget) {
374
0
    NS_ERROR("invalid id in selection event");
375
0
    return IPC_OK();
376
0
  }
377
0
378
0
  ProxySelectionEvent(target, widget, aType);
379
0
  if (!nsCoreUtils::AccEventObserversExist()) {
380
0
    return IPC_OK();
381
0
  }
382
0
  xpcAccessibleGeneric* xpcTarget = GetXPCAccessible(target);
383
0
  xpcAccessibleDocument* xpcDoc = GetAccService()->GetXPCDocument(this);
384
0
  RefPtr<xpcAccEvent> event = new xpcAccEvent(aType, xpcTarget, xpcDoc,
385
0
                                              nullptr, false);
386
0
  nsCoreUtils::DispatchAccEvent(std::move(event));
387
0
388
0
  return IPC_OK();
389
0
}
390
391
mozilla::ipc::IPCResult
392
DocAccessibleParent::RecvVirtualCursorChangeEvent(const uint64_t& aID,
393
                                                  const uint64_t& aOldPositionID,
394
                                                  const int32_t& aOldStartOffset,
395
                                                  const int32_t& aOldEndOffset,
396
                                                  const uint64_t& aNewPositionID,
397
                                                  const int32_t& aNewStartOffset,
398
                                                  const int32_t& aNewEndOffset,
399
                                                  const int16_t& aReason,
400
                                                  const int16_t& aBoundaryType,
401
                                                  const bool& aFromUser)
402
0
{
403
0
  ProxyAccessible* target = GetAccessible(aID);
404
0
  ProxyAccessible* oldPosition = GetAccessible(aOldPositionID);
405
0
  ProxyAccessible* newPosition = GetAccessible(aNewPositionID);
406
0
407
0
  if (!target) {
408
0
    NS_ERROR("no proxy for event!");
409
0
    return IPC_OK();
410
0
  }
411
0
412
#if defined(ANDROID)
413
  ProxyVirtualCursorChangeEvent(target,
414
                                oldPosition, aOldStartOffset, aOldEndOffset,
415
                                newPosition, aNewStartOffset, aNewEndOffset,
416
                                aReason, aBoundaryType, aFromUser);
417
#endif
418
419
0
  if (!nsCoreUtils::AccEventObserversExist()) {
420
0
    return IPC_OK();
421
0
  }
422
0
423
0
  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
424
0
  RefPtr<xpcAccVirtualCursorChangeEvent> event =
425
0
    new xpcAccVirtualCursorChangeEvent(nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED,
426
0
                                       GetXPCAccessible(target), doc,
427
0
                                       nullptr, aFromUser,
428
0
                                       GetXPCAccessible(oldPosition),
429
0
                                       aOldStartOffset, aOldEndOffset,
430
0
                                       GetXPCAccessible(newPosition),
431
0
                                       aNewStartOffset, aNewEndOffset,
432
0
                                       aBoundaryType, aReason);
433
0
  nsCoreUtils::DispatchAccEvent(std::move(event));
434
0
435
0
  return IPC_OK();
436
0
}
437
438
mozilla::ipc::IPCResult
439
DocAccessibleParent::RecvScrollingEvent(const uint64_t& aID,
440
                                        const uint64_t& aType,
441
                                        const uint32_t& aScrollX,
442
                                        const uint32_t& aScrollY,
443
                                        const uint32_t& aMaxScrollX,
444
                                        const uint32_t& aMaxScrollY)
445
0
{
446
0
  ProxyAccessible* target = GetAccessible(aID);
447
0
448
0
  if (!target) {
449
0
    NS_ERROR("no proxy for event!");
450
0
    return IPC_OK();
451
0
  }
452
0
453
#if defined(ANDROID)
454
  ProxyScrollingEvent(target, aType, aScrollX, aScrollY, aMaxScrollX, aMaxScrollY);
455
#endif
456
457
0
  if (!nsCoreUtils::AccEventObserversExist()) {
458
0
    return IPC_OK();
459
0
  }
460
0
461
0
  xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
462
0
  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
463
0
  nsINode* node = nullptr;
464
0
  bool fromUser = true; // XXX: Determine if this was from user input.
465
0
  RefPtr<xpcAccScrollingEvent> event =
466
0
    new xpcAccScrollingEvent(aType, xpcAcc, doc, node, fromUser, aScrollX,
467
0
                             aScrollY, aMaxScrollX, aMaxScrollY);
468
0
  nsCoreUtils::DispatchAccEvent(std::move(event));
469
0
470
0
  return IPC_OK();
471
0
}
472
473
mozilla::ipc::IPCResult
474
DocAccessibleParent::RecvRoleChangedEvent(const a11y::role& aRole)
475
0
{
476
0
  if (mShutdown) {
477
0
    return IPC_OK();
478
0
  }
479
0
480
0
  mRole = aRole;
481
0
  return IPC_OK();
482
0
}
483
484
mozilla::ipc::IPCResult
485
DocAccessibleParent::RecvBindChildDoc(PDocAccessibleParent* aChildDoc, const uint64_t& aID)
486
0
{
487
0
  // One document should never directly be the child of another.
488
0
  // We should always have at least an outer doc accessible in between.
489
0
  MOZ_ASSERT(aID);
490
0
  if (!aID)
491
0
    return IPC_FAIL(this, "ID is 0!");
492
0
493
0
  if (mShutdown) {
494
0
    return IPC_OK();
495
0
  }
496
0
497
0
  MOZ_ASSERT(CheckDocTree());
498
0
499
0
  auto childDoc = static_cast<DocAccessibleParent*>(aChildDoc);
500
0
  childDoc->Unbind();
501
0
  ipc::IPCResult result = AddChildDoc(childDoc, aID, false);
502
0
  MOZ_ASSERT(result);
503
0
  MOZ_ASSERT(CheckDocTree());
504
#ifdef DEBUG
505
  if (!result) {
506
    return result;
507
  }
508
#else
509
0
  result = IPC_OK();
510
0
#endif
511
0
512
0
  return result;
513
0
}
514
515
ipc::IPCResult
516
DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc,
517
                                 uint64_t aParentID, bool aCreating)
518
0
{
519
0
  // We do not use GetAccessible here because we want to be sure to not get the
520
0
  // document it self.
521
0
  ProxyEntry* e = mAccessibles.GetEntry(aParentID);
522
0
  if (!e) {
523
0
    return IPC_FAIL(this, "binding to nonexistant proxy!");
524
0
  }
525
0
526
0
  ProxyAccessible* outerDoc = e->mProxy;
527
0
  MOZ_ASSERT(outerDoc);
528
0
529
0
  // OuterDocAccessibles are expected to only have a document as a child.
530
0
  // However for compatibility we tolerate replacing one document with another
531
0
  // here.
532
0
  if (outerDoc->ChildrenCount() > 1 ||
533
0
      (outerDoc->ChildrenCount() == 1 && !outerDoc->ChildAt(0)->IsDoc())) {
534
0
    return IPC_FAIL(this, "binding to proxy that can't be a outerDoc!");
535
0
  }
536
0
537
0
  if (outerDoc->ChildrenCount() == 1) {
538
0
    MOZ_ASSERT(outerDoc->ChildAt(0)->AsDoc());
539
0
    outerDoc->ChildAt(0)->AsDoc()->Unbind();
540
0
  }
541
0
542
0
  aChildDoc->SetParent(outerDoc);
543
0
  outerDoc->SetChildDoc(aChildDoc);
544
0
  mChildDocs.AppendElement(aChildDoc->mActorID);
545
0
  aChildDoc->mParentDoc = mActorID;
546
0
547
0
  if (aCreating) {
548
0
    ProxyCreated(aChildDoc, Interfaces::DOCUMENT | Interfaces::HYPERTEXT);
549
0
  }
550
0
551
0
  return IPC_OK();
552
0
}
553
554
mozilla::ipc::IPCResult
555
DocAccessibleParent::RecvShutdown()
556
0
{
557
0
  Destroy();
558
0
559
0
  auto mgr = static_cast<dom::TabParent*>(Manager());
560
0
  if (!mgr->IsDestroyed()) {
561
0
    if (!PDocAccessibleParent::Send__delete__(this)) {
562
0
      return IPC_FAIL_NO_REASON(mgr);
563
0
    }
564
0
  }
565
0
566
0
  return IPC_OK();
567
0
}
568
569
void
570
DocAccessibleParent::Destroy()
571
0
{
572
0
  // If we are already shutdown that is because our containing tab parent is
573
0
  // shutting down in which case we don't need to do anything.
574
0
  if (mShutdown) {
575
0
    return;
576
0
  }
577
0
578
0
  mShutdown = true;
579
0
580
0
  MOZ_DIAGNOSTIC_ASSERT(LiveDocs().Contains(mActorID));
581
0
  uint32_t childDocCount = mChildDocs.Length();
582
0
  for (uint32_t i = 0; i < childDocCount; i++) {
583
0
    for (uint32_t j = i + 1; j < childDocCount; j++) {
584
0
      MOZ_DIAGNOSTIC_ASSERT(mChildDocs[i] != mChildDocs[j]);
585
0
    }
586
0
  }
587
0
588
0
  // XXX This indirection through the hash map of live documents shouldn't be
589
0
  // needed, but be paranoid for now.
590
0
  int32_t actorID = mActorID;
591
0
  for (uint32_t i = childDocCount - 1; i < childDocCount; i--) {
592
0
    DocAccessibleParent* thisDoc = LiveDocs().Get(actorID);
593
0
    MOZ_ASSERT(thisDoc);
594
0
    if (!thisDoc) {
595
0
      return;
596
0
    }
597
0
598
0
    thisDoc->ChildDocAt(i)->Destroy();
599
0
  }
600
0
601
0
  for (auto iter = mAccessibles.Iter(); !iter.Done(); iter.Next()) {
602
0
    MOZ_ASSERT(iter.Get()->mProxy != this);
603
0
    ProxyDestroyed(iter.Get()->mProxy);
604
0
    iter.Remove();
605
0
  }
606
0
607
0
  DocAccessibleParent* thisDoc = LiveDocs().Get(actorID);
608
0
  MOZ_ASSERT(thisDoc);
609
0
  if (!thisDoc) {
610
0
    return;
611
0
  }
612
0
613
0
  // The code above should have already completely cleared these, but to be
614
0
  // extra safe make sure they are cleared here.
615
0
  thisDoc->mAccessibles.Clear();
616
0
  thisDoc->mChildDocs.Clear();
617
0
618
0
  DocManager::NotifyOfRemoteDocShutdown(thisDoc);
619
0
  thisDoc = LiveDocs().Get(actorID);
620
0
  MOZ_ASSERT(thisDoc);
621
0
  if (!thisDoc) {
622
0
    return;
623
0
  }
624
0
625
0
  ProxyDestroyed(thisDoc);
626
0
  thisDoc = LiveDocs().Get(actorID);
627
0
  MOZ_ASSERT(thisDoc);
628
0
  if (!thisDoc) {
629
0
    return;
630
0
  }
631
0
632
0
  if (DocAccessibleParent* parentDoc = thisDoc->ParentDoc())
633
0
    parentDoc->RemoveChildDoc(thisDoc);
634
0
  else if (IsTopLevel())
635
0
    GetAccService()->RemoteDocShutdown(this);
636
0
}
637
638
DocAccessibleParent*
639
DocAccessibleParent::ParentDoc() const
640
0
{
641
0
  if (mParentDoc == kNoParentDoc) {
642
0
    return nullptr;
643
0
  }
644
0
645
0
  return LiveDocs().Get(mParentDoc);
646
0
}
647
648
bool
649
DocAccessibleParent::CheckDocTree() const
650
0
{
651
0
  size_t childDocs = mChildDocs.Length();
652
0
  for (size_t i = 0; i < childDocs; i++) {
653
0
    const DocAccessibleParent* childDoc = ChildDocAt(i);
654
0
    if (!childDoc || childDoc->ParentDoc() != this)
655
0
      return false;
656
0
657
0
    if (!childDoc->CheckDocTree()) {
658
0
      return false;
659
0
    }
660
0
  }
661
0
662
0
  return true;
663
0
}
664
665
xpcAccessibleGeneric*
666
DocAccessibleParent::GetXPCAccessible(ProxyAccessible* aProxy)
667
0
{
668
0
  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
669
0
  MOZ_ASSERT(doc);
670
0
671
0
  return doc->GetXPCAccessible(aProxy);
672
0
}
673
674
#if defined(XP_WIN)
675
void
676
DocAccessibleParent::MaybeInitWindowEmulation()
677
{
678
  if (!nsWinUtils::IsWindowEmulationStarted()) {
679
    return;
680
  }
681
682
  // XXX get the bounds from the tabParent instead of poking at accessibles
683
  // which might not exist yet.
684
  Accessible* outerDoc = OuterDocOfRemoteBrowser();
685
  if (!outerDoc) {
686
    return;
687
  }
688
689
  RootAccessible* rootDocument = outerDoc->RootAccessible();
690
  MOZ_ASSERT(rootDocument);
691
692
  bool isActive = true;
693
  nsIntRect rect(CW_USEDEFAULT, CW_USEDEFAULT, 0, 0);
694
  if (Compatibility::IsDolphin()) {
695
    rect = Bounds();
696
    nsIntRect rootRect = rootDocument->Bounds();
697
    rect.MoveToX(rootRect.X() - rect.X());
698
    rect.MoveToY(rect.Y() - rootRect.Y());
699
700
    auto tab = static_cast<dom::TabParent*>(Manager());
701
    tab->GetDocShellIsActive(&isActive);
702
  }
703
704
  nsWinUtils::NativeWindowCreateProc onCreate([this](HWND aHwnd) -> void {
705
    IDispatchHolder hWndAccHolder;
706
707
    ::SetPropW(aHwnd, kPropNameDocAccParent, reinterpret_cast<HANDLE>(this));
708
709
    SetEmulatedWindowHandle(aHwnd);
710
711
    RefPtr<IAccessible> hwndAcc;
712
    if (SUCCEEDED(::AccessibleObjectFromWindow(aHwnd, OBJID_WINDOW,
713
                                               IID_IAccessible,
714
                                               getter_AddRefs(hwndAcc)))) {
715
      RefPtr<IDispatch> wrapped(mscom::PassthruProxy::Wrap<IDispatch>(WrapNotNull(hwndAcc)));
716
      hWndAccHolder.Set(IDispatchHolder::COMPtrType(mscom::ToProxyUniquePtr(std::move(wrapped))));
717
    }
718
719
    Unused << SendEmulatedWindow(reinterpret_cast<uintptr_t>(mEmulatedWindowHandle),
720
                                 hWndAccHolder);
721
  });
722
723
  HWND parentWnd = reinterpret_cast<HWND>(rootDocument->GetNativeWindow());
724
  DebugOnly<HWND> hWnd = nsWinUtils::CreateNativeWindow(kClassNameTabContent,
725
                                                        parentWnd,
726
                                                        rect.X(), rect.Y(),
727
                                                        rect.Width(), rect.Height(),
728
                                                        isActive, &onCreate);
729
  MOZ_ASSERT(hWnd);
730
}
731
732
/**
733
 * @param aCOMProxy COM Proxy to the document in the content process.
734
 */
735
void
736
DocAccessibleParent::SendParentCOMProxy()
737
{
738
  // Make sure that we're not racing with a tab shutdown
739
  auto tab = static_cast<dom::TabParent*>(Manager());
740
  MOZ_ASSERT(tab);
741
  if (tab->IsDestroyed()) {
742
    return;
743
  }
744
745
  Accessible* outerDoc = OuterDocOfRemoteBrowser();
746
  if (!outerDoc) {
747
    return;
748
  }
749
750
  RefPtr<IAccessible> nativeAcc;
751
  outerDoc->GetNativeInterface(getter_AddRefs(nativeAcc));
752
  MOZ_ASSERT(nativeAcc);
753
754
  RefPtr<IDispatch> wrapped(mscom::PassthruProxy::Wrap<IDispatch>(WrapNotNull(nativeAcc)));
755
756
  IDispatchHolder::COMPtrType ptr(mscom::ToProxyUniquePtr(std::move(wrapped)));
757
  IDispatchHolder holder(std::move(ptr));
758
  if (!PDocAccessibleParent::SendParentCOMProxy(holder)) {
759
    return;
760
  }
761
762
#if defined(MOZ_CONTENT_SANDBOX)
763
  mParentProxyStream = holder.GetPreservedStream();
764
#endif // defined(MOZ_CONTENT_SANDBOX)
765
}
766
767
void
768
DocAccessibleParent::SetEmulatedWindowHandle(HWND aWindowHandle)
769
{
770
  if (!aWindowHandle && mEmulatedWindowHandle && IsTopLevel()) {
771
    ::DestroyWindow(mEmulatedWindowHandle);
772
  }
773
  mEmulatedWindowHandle = aWindowHandle;
774
}
775
776
mozilla::ipc::IPCResult
777
DocAccessibleParent::RecvGetWindowedPluginIAccessible(
778
      const WindowsHandle& aHwnd, IAccessibleHolder* aPluginCOMProxy)
779
{
780
#if defined(MOZ_CONTENT_SANDBOX)
781
  // We don't actually want the accessible object for aHwnd, but rather the
782
  // one that belongs to its child (see HTMLWin32ObjectAccessible).
783
  HWND childWnd = ::GetWindow(reinterpret_cast<HWND>(aHwnd), GW_CHILD);
784
  if (!childWnd) {
785
    // We're seeing this in the wild - the plugin is windowed but we no longer
786
    // have a window.
787
    return IPC_OK();
788
  }
789
790
  IAccessible* rawAccPlugin = nullptr;
791
  HRESULT hr = ::AccessibleObjectFromWindow(childWnd, OBJID_WINDOW,
792
                                            IID_IAccessible,
793
                                            (void**)&rawAccPlugin);
794
  if (FAILED(hr)) {
795
    // This might happen if the plugin doesn't handle WM_GETOBJECT properly.
796
    // We should not consider that a failure.
797
    return IPC_OK();
798
  }
799
800
  aPluginCOMProxy->Set(IAccessibleHolder::COMPtrType(rawAccPlugin));
801
802
  return IPC_OK();
803
#else
804
  return IPC_FAIL(this, "Message unsupported in this build configuration");
805
#endif
806
}
807
808
mozilla::ipc::IPCResult
809
DocAccessibleParent::RecvFocusEvent(const uint64_t& aID,
810
                                    const LayoutDeviceIntRect& aCaretRect)
811
{
812
  if (mShutdown) {
813
    return IPC_OK();
814
  }
815
816
  ProxyAccessible* proxy = GetAccessible(aID);
817
  if (!proxy) {
818
    NS_ERROR("no proxy for event!");
819
    return IPC_OK();
820
  }
821
822
  ProxyFocusEvent(proxy, aCaretRect);
823
824
  if (!nsCoreUtils::AccEventObserversExist()) {
825
    return IPC_OK();
826
  }
827
828
  xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
829
  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
830
  nsINode* node = nullptr;
831
  bool fromUser = true; // XXX fix me
832
  RefPtr<xpcAccEvent> event = new xpcAccEvent(nsIAccessibleEvent::EVENT_FOCUS,
833
                                              xpcAcc, doc, node, fromUser);
834
  nsCoreUtils::DispatchAccEvent(std::move(event));
835
836
  return IPC_OK();
837
}
838
839
#endif // defined(XP_WIN)
840
841
} // a11y
842
} // mozilla