Bug 1557368 [wpt PR 16856] - Update error type test for attachInternals(), a=testonly
[gecko.git] / accessible / ipc / DocAccessibleParent.cpp
blob88ab834963281bd8eecc55a61b5ccd08dc357dcf
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/. */
7 #include "DocAccessibleParent.h"
8 #include "mozilla/a11y/Platform.h"
9 #include "mozilla/dom/BrowserBridgeParent.h"
10 #include "mozilla/dom/BrowserParent.h"
11 #include "xpcAccessibleDocument.h"
12 #include "xpcAccEvents.h"
13 #include "nsAccUtils.h"
14 #include "nsCoreUtils.h"
16 #if defined(XP_WIN)
17 # include "AccessibleWrap.h"
18 # include "Compatibility.h"
19 # include "mozilla/mscom/PassthruProxy.h"
20 # include "mozilla/mscom/Ptr.h"
21 # include "nsWinUtils.h"
22 # include "RootAccessible.h"
23 #endif
25 namespace mozilla {
26 namespace a11y {
27 uint64_t DocAccessibleParent::sMaxDocID = 0;
29 mozilla::ipc::IPCResult DocAccessibleParent::RecvShowEvent(
30 const ShowEventData& aData, const bool& aFromUser) {
31 if (mShutdown) return IPC_OK();
33 MOZ_ASSERT(CheckDocTree());
35 if (aData.NewTree().IsEmpty()) {
36 return IPC_FAIL(this, "No children being added");
39 ProxyAccessible* parent = GetAccessible(aData.ID());
41 // XXX This should really never happen, but sometimes we fail to fire the
42 // required show events.
43 if (!parent) {
44 NS_ERROR("adding child to unknown accessible");
45 #ifdef DEBUG
46 return IPC_FAIL(this, "unknown parent accessible");
47 #else
48 return IPC_OK();
49 #endif
52 uint32_t newChildIdx = aData.Idx();
53 if (newChildIdx > parent->ChildrenCount()) {
54 NS_ERROR("invalid index to add child at");
55 #ifdef DEBUG
56 return IPC_FAIL(this, "invalid index");
57 #else
58 return IPC_OK();
59 #endif
62 uint32_t consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx);
63 MOZ_ASSERT(consumed == aData.NewTree().Length());
65 // XXX This shouldn't happen, but if we failed to add children then the below
66 // is pointless and can crash.
67 if (!consumed) {
68 return IPC_FAIL(this, "failed to add children");
71 #ifdef DEBUG
72 for (uint32_t i = 0; i < consumed; i++) {
73 uint64_t id = aData.NewTree()[i].ID();
74 MOZ_ASSERT(mAccessibles.GetEntry(id));
76 #endif
78 MOZ_ASSERT(CheckDocTree());
80 // Just update, no events.
81 if (aData.EventSuppressed()) {
82 return IPC_OK();
85 ProxyAccessible* target = parent->ChildAt(newChildIdx);
86 ProxyShowHideEvent(target, parent, true, aFromUser);
88 if (!nsCoreUtils::AccEventObserversExist()) {
89 return IPC_OK();
92 uint32_t type = nsIAccessibleEvent::EVENT_SHOW;
93 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
94 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
95 nsINode* node = nullptr;
96 RefPtr<xpcAccEvent> event =
97 new xpcAccEvent(type, xpcAcc, doc, node, aFromUser);
98 nsCoreUtils::DispatchAccEvent(std::move(event));
100 return IPC_OK();
103 uint32_t DocAccessibleParent::AddSubtree(
104 ProxyAccessible* aParent, const nsTArray<a11y::AccessibleData>& aNewTree,
105 uint32_t aIdx, uint32_t aIdxInParent) {
106 if (aNewTree.Length() <= aIdx) {
107 NS_ERROR("bad index in serialized tree!");
108 return 0;
111 const AccessibleData& newChild = aNewTree[aIdx];
113 if (mAccessibles.Contains(newChild.ID())) {
114 NS_ERROR("ID already in use");
115 return 0;
118 ProxyAccessible* newProxy = new ProxyAccessible(
119 newChild.ID(), aParent, this, newChild.Role(), newChild.Interfaces());
121 aParent->AddChildAt(aIdxInParent, newProxy);
122 mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy;
123 ProxyCreated(newProxy, newChild.Interfaces());
125 #if defined(XP_WIN)
126 WrapperFor(newProxy)->SetID(newChild.MsaaID());
127 #endif
129 uint32_t accessibles = 1;
130 uint32_t kids = newChild.ChildrenCount();
131 for (uint32_t i = 0; i < kids; i++) {
132 uint32_t consumed = AddSubtree(newProxy, aNewTree, aIdx + accessibles, i);
133 if (!consumed) return 0;
135 accessibles += consumed;
138 MOZ_ASSERT(newProxy->ChildrenCount() == kids);
140 return accessibles;
143 mozilla::ipc::IPCResult DocAccessibleParent::RecvHideEvent(
144 const uint64_t& aRootID, const bool& aFromUser) {
145 if (mShutdown) return IPC_OK();
147 MOZ_ASSERT(CheckDocTree());
149 // We shouldn't actually need this because mAccessibles shouldn't have an
150 // entry for the document itself, but it doesn't hurt to be explicit.
151 if (!aRootID) {
152 return IPC_FAIL(this, "Trying to hide entire document?");
155 ProxyEntry* rootEntry = mAccessibles.GetEntry(aRootID);
156 if (!rootEntry) {
157 NS_ERROR("invalid root being removed!");
158 return IPC_OK();
161 ProxyAccessible* root = rootEntry->mProxy;
162 if (!root) {
163 NS_ERROR("invalid root being removed!");
164 return IPC_OK();
167 ProxyAccessible* parent = root->Parent();
168 ProxyShowHideEvent(root, parent, false, aFromUser);
170 RefPtr<xpcAccHideEvent> event = nullptr;
171 if (nsCoreUtils::AccEventObserversExist()) {
172 uint32_t type = nsIAccessibleEvent::EVENT_HIDE;
173 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(root);
174 xpcAccessibleGeneric* xpcParent = GetXPCAccessible(parent);
175 ProxyAccessible* next = root->NextSibling();
176 xpcAccessibleGeneric* xpcNext = next ? GetXPCAccessible(next) : nullptr;
177 ProxyAccessible* prev = root->PrevSibling();
178 xpcAccessibleGeneric* xpcPrev = prev ? GetXPCAccessible(prev) : nullptr;
179 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
180 nsINode* node = nullptr;
181 event = new xpcAccHideEvent(type, xpcAcc, doc, node, aFromUser, xpcParent,
182 xpcNext, xpcPrev);
185 parent->RemoveChild(root);
186 root->Shutdown();
188 MOZ_ASSERT(CheckDocTree());
190 if (event) {
191 nsCoreUtils::DispatchAccEvent(std::move(event));
194 return IPC_OK();
197 mozilla::ipc::IPCResult DocAccessibleParent::RecvEvent(
198 const uint64_t& aID, const uint32_t& aEventType) {
199 if (mShutdown) {
200 return IPC_OK();
203 ProxyAccessible* proxy = GetAccessible(aID);
204 if (!proxy) {
205 NS_ERROR("no proxy for event!");
206 return IPC_OK();
209 ProxyEvent(proxy, aEventType);
211 if (!nsCoreUtils::AccEventObserversExist()) {
212 return IPC_OK();
215 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
216 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
217 nsINode* node = nullptr;
218 bool fromUser = true; // XXX fix me
219 RefPtr<xpcAccEvent> event =
220 new xpcAccEvent(aEventType, xpcAcc, doc, node, fromUser);
221 nsCoreUtils::DispatchAccEvent(std::move(event));
223 return IPC_OK();
226 mozilla::ipc::IPCResult DocAccessibleParent::RecvStateChangeEvent(
227 const uint64_t& aID, const uint64_t& aState, const bool& aEnabled) {
228 if (mShutdown) {
229 return IPC_OK();
232 ProxyAccessible* target = GetAccessible(aID);
233 if (!target) {
234 NS_ERROR("we don't know about the target of a state change event!");
235 return IPC_OK();
238 ProxyStateChangeEvent(target, aState, aEnabled);
240 if (!nsCoreUtils::AccEventObserversExist()) {
241 return IPC_OK();
244 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
245 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
246 uint32_t type = nsIAccessibleEvent::EVENT_STATE_CHANGE;
247 bool extra;
248 uint32_t state = nsAccUtils::To32States(aState, &extra);
249 bool fromUser = true; // XXX fix this
250 nsINode* node = nullptr; // XXX can we do better?
251 RefPtr<xpcAccStateChangeEvent> event = new xpcAccStateChangeEvent(
252 type, xpcAcc, doc, node, fromUser, state, extra, aEnabled);
253 nsCoreUtils::DispatchAccEvent(std::move(event));
255 return IPC_OK();
258 mozilla::ipc::IPCResult DocAccessibleParent::RecvCaretMoveEvent(
259 const uint64_t& aID,
260 #if defined(XP_WIN)
261 const LayoutDeviceIntRect& aCaretRect,
262 #endif // defined (XP_WIN)
263 const int32_t& aOffset) {
264 if (mShutdown) {
265 return IPC_OK();
268 ProxyAccessible* proxy = GetAccessible(aID);
269 if (!proxy) {
270 NS_ERROR("unknown caret move event target!");
271 return IPC_OK();
274 #if defined(XP_WIN)
275 ProxyCaretMoveEvent(proxy, aCaretRect);
276 #else
277 ProxyCaretMoveEvent(proxy, aOffset);
278 #endif
280 if (!nsCoreUtils::AccEventObserversExist()) {
281 return IPC_OK();
284 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
285 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
286 nsINode* node = nullptr;
287 bool fromUser = true; // XXX fix me
288 uint32_t type = nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED;
289 RefPtr<xpcAccCaretMoveEvent> event =
290 new xpcAccCaretMoveEvent(type, xpcAcc, doc, node, fromUser, aOffset);
291 nsCoreUtils::DispatchAccEvent(std::move(event));
293 return IPC_OK();
296 mozilla::ipc::IPCResult DocAccessibleParent::RecvTextChangeEvent(
297 const uint64_t& aID, const nsString& aStr, const int32_t& aStart,
298 const uint32_t& aLen, const bool& aIsInsert, const bool& aFromUser) {
299 if (mShutdown) {
300 return IPC_OK();
303 ProxyAccessible* target = GetAccessible(aID);
304 if (!target) {
305 NS_ERROR("text change event target is unknown!");
306 return IPC_OK();
309 ProxyTextChangeEvent(target, aStr, aStart, aLen, aIsInsert, aFromUser);
311 if (!nsCoreUtils::AccEventObserversExist()) {
312 return IPC_OK();
315 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
316 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
317 uint32_t type = aIsInsert ? nsIAccessibleEvent::EVENT_TEXT_INSERTED
318 : nsIAccessibleEvent::EVENT_TEXT_REMOVED;
319 nsINode* node = nullptr;
320 RefPtr<xpcAccTextChangeEvent> event = new xpcAccTextChangeEvent(
321 type, xpcAcc, doc, node, aFromUser, aStart, aLen, aIsInsert, aStr);
322 nsCoreUtils::DispatchAccEvent(std::move(event));
324 return IPC_OK();
327 #if defined(XP_WIN)
329 mozilla::ipc::IPCResult DocAccessibleParent::RecvSyncTextChangeEvent(
330 const uint64_t& aID, const nsString& aStr, const int32_t& aStart,
331 const uint32_t& aLen, const bool& aIsInsert, const bool& aFromUser) {
332 return RecvTextChangeEvent(aID, aStr, aStart, aLen, aIsInsert, aFromUser);
335 #endif // defined(XP_WIN)
337 mozilla::ipc::IPCResult DocAccessibleParent::RecvSelectionEvent(
338 const uint64_t& aID, const uint64_t& aWidgetID, const uint32_t& aType) {
339 if (mShutdown) {
340 return IPC_OK();
343 ProxyAccessible* target = GetAccessible(aID);
344 ProxyAccessible* widget = GetAccessible(aWidgetID);
345 if (!target || !widget) {
346 NS_ERROR("invalid id in selection event");
347 return IPC_OK();
350 ProxySelectionEvent(target, widget, aType);
351 if (!nsCoreUtils::AccEventObserversExist()) {
352 return IPC_OK();
354 xpcAccessibleGeneric* xpcTarget = GetXPCAccessible(target);
355 xpcAccessibleDocument* xpcDoc = GetAccService()->GetXPCDocument(this);
356 RefPtr<xpcAccEvent> event =
357 new xpcAccEvent(aType, xpcTarget, xpcDoc, nullptr, false);
358 nsCoreUtils::DispatchAccEvent(std::move(event));
360 return IPC_OK();
363 mozilla::ipc::IPCResult DocAccessibleParent::RecvVirtualCursorChangeEvent(
364 const uint64_t& aID, const uint64_t& aOldPositionID,
365 const int32_t& aOldStartOffset, const int32_t& aOldEndOffset,
366 const uint64_t& aNewPositionID, const int32_t& aNewStartOffset,
367 const int32_t& aNewEndOffset, const int16_t& aReason,
368 const int16_t& aBoundaryType, const bool& aFromUser) {
369 ProxyAccessible* target = GetAccessible(aID);
370 ProxyAccessible* oldPosition = GetAccessible(aOldPositionID);
371 ProxyAccessible* newPosition = GetAccessible(aNewPositionID);
373 if (!target) {
374 NS_ERROR("no proxy for event!");
375 return IPC_OK();
378 #if defined(ANDROID)
379 ProxyVirtualCursorChangeEvent(
380 target, oldPosition, aOldStartOffset, aOldEndOffset, newPosition,
381 aNewStartOffset, aNewEndOffset, aReason, aBoundaryType, aFromUser);
382 #endif
384 if (!nsCoreUtils::AccEventObserversExist()) {
385 return IPC_OK();
388 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
389 RefPtr<xpcAccVirtualCursorChangeEvent> event =
390 new xpcAccVirtualCursorChangeEvent(
391 nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED,
392 GetXPCAccessible(target), doc, nullptr, aFromUser,
393 GetXPCAccessible(oldPosition), aOldStartOffset, aOldEndOffset,
394 GetXPCAccessible(newPosition), aNewStartOffset, aNewEndOffset,
395 aBoundaryType, aReason);
396 nsCoreUtils::DispatchAccEvent(std::move(event));
398 return IPC_OK();
401 mozilla::ipc::IPCResult DocAccessibleParent::RecvScrollingEvent(
402 const uint64_t& aID, const uint64_t& aType, const uint32_t& aScrollX,
403 const uint32_t& aScrollY, const uint32_t& aMaxScrollX,
404 const uint32_t& aMaxScrollY) {
405 ProxyAccessible* target = GetAccessible(aID);
407 if (!target) {
408 NS_ERROR("no proxy for event!");
409 return IPC_OK();
412 #if defined(ANDROID)
413 ProxyScrollingEvent(target, aType, aScrollX, aScrollY, aMaxScrollX,
414 aMaxScrollY);
415 #else
416 ProxyEvent(target, aType);
417 #endif
419 if (!nsCoreUtils::AccEventObserversExist()) {
420 return IPC_OK();
423 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
424 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
425 nsINode* node = nullptr;
426 bool fromUser = true; // XXX: Determine if this was from user input.
427 RefPtr<xpcAccScrollingEvent> event =
428 new xpcAccScrollingEvent(aType, xpcAcc, doc, node, fromUser, aScrollX,
429 aScrollY, aMaxScrollX, aMaxScrollY);
430 nsCoreUtils::DispatchAccEvent(std::move(event));
432 return IPC_OK();
435 #if !defined(XP_WIN)
436 mozilla::ipc::IPCResult DocAccessibleParent::RecvAnnouncementEvent(
437 const uint64_t& aID, const nsString& aAnnouncement,
438 const uint16_t& aPriority) {
439 ProxyAccessible* target = GetAccessible(aID);
441 if (!target) {
442 NS_ERROR("no proxy for event!");
443 return IPC_OK();
446 # if defined(ANDROID)
447 ProxyAnnouncementEvent(target, aAnnouncement, aPriority);
448 # endif
450 if (!nsCoreUtils::AccEventObserversExist()) {
451 return IPC_OK();
454 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
455 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
456 RefPtr<xpcAccAnnouncementEvent> event = new xpcAccAnnouncementEvent(
457 nsIAccessibleEvent::EVENT_ANNOUNCEMENT, xpcAcc, doc, nullptr, false,
458 aAnnouncement, aPriority);
459 nsCoreUtils::DispatchAccEvent(std::move(event));
461 return IPC_OK();
463 #endif
465 mozilla::ipc::IPCResult DocAccessibleParent::RecvRoleChangedEvent(
466 const a11y::role& aRole) {
467 if (mShutdown) {
468 return IPC_OK();
471 mRole = aRole;
472 return IPC_OK();
475 mozilla::ipc::IPCResult DocAccessibleParent::RecvBindChildDoc(
476 PDocAccessibleParent* aChildDoc, const uint64_t& aID) {
477 // One document should never directly be the child of another.
478 // We should always have at least an outer doc accessible in between.
479 MOZ_ASSERT(aID);
480 if (!aID) return IPC_FAIL(this, "ID is 0!");
482 if (mShutdown) {
483 return IPC_OK();
486 MOZ_ASSERT(CheckDocTree());
488 auto childDoc = static_cast<DocAccessibleParent*>(aChildDoc);
489 childDoc->Unbind();
490 ipc::IPCResult result = AddChildDoc(childDoc, aID, false);
491 MOZ_ASSERT(result);
492 MOZ_ASSERT(CheckDocTree());
493 #ifdef DEBUG
494 if (!result) {
495 return result;
497 #else
498 result = IPC_OK();
499 #endif
501 return result;
504 ipc::IPCResult DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc,
505 uint64_t aParentID,
506 bool aCreating) {
507 // We do not use GetAccessible here because we want to be sure to not get the
508 // document it self.
509 ProxyEntry* e = mAccessibles.GetEntry(aParentID);
510 if (!e) {
511 return IPC_FAIL(this, "binding to nonexistant proxy!");
514 ProxyAccessible* outerDoc = e->mProxy;
515 MOZ_ASSERT(outerDoc);
517 // OuterDocAccessibles are expected to only have a document as a child.
518 // However for compatibility we tolerate replacing one document with another
519 // here.
520 if (outerDoc->ChildrenCount() > 1 ||
521 (outerDoc->ChildrenCount() == 1 && !outerDoc->ChildAt(0)->IsDoc())) {
522 return IPC_FAIL(this, "binding to proxy that can't be a outerDoc!");
525 if (outerDoc->ChildrenCount() == 1) {
526 MOZ_ASSERT(outerDoc->ChildAt(0)->AsDoc());
527 outerDoc->ChildAt(0)->AsDoc()->Unbind();
530 aChildDoc->SetParent(outerDoc);
531 outerDoc->SetChildDoc(aChildDoc);
532 mChildDocs.AppendElement(aChildDoc->mActorID);
533 aChildDoc->mParentDoc = mActorID;
535 if (aCreating) {
536 ProxyCreated(aChildDoc, Interfaces::DOCUMENT | Interfaces::HYPERTEXT);
539 #if defined(XP_WIN)
540 auto embeddedBrowser = static_cast<dom::BrowserParent*>(aChildDoc->Manager());
541 dom::BrowserBridgeParent* bridge = embeddedBrowser->GetBrowserBridgeParent();
542 if (bridge) {
543 // aChildDoc is an embedded document in a different content process to
544 // this document.
545 // Send a COM proxy for the embedded document to the embedder process
546 // hosting the iframe. This will be returned as the child of the
547 // embedder OuterDocAccessible.
548 RefPtr<IDispatch> docAcc;
549 aChildDoc->GetCOMInterface((void**)getter_AddRefs(docAcc));
550 RefPtr<IDispatch> docWrapped(
551 mscom::PassthruProxy::Wrap<IDispatch>(WrapNotNull(docAcc)));
552 IDispatchHolder::COMPtrType docPtr(
553 mscom::ToProxyUniquePtr(std::move(docWrapped)));
554 IDispatchHolder docHolder(std::move(docPtr));
555 if (bridge->SendSetEmbeddedDocAccessibleCOMProxy(docHolder)) {
556 # if defined(MOZ_SANDBOX)
557 mDocProxyStream = docHolder.GetPreservedStream();
558 # endif // defined(MOZ_SANDBOX)
560 // Send a COM proxy for the embedder OuterDocAccessible to the embedded
561 // document process. This will be returned as the parent of the
562 // embedded document.
563 aChildDoc->SendParentCOMProxy(WrapperFor(outerDoc));
564 if (nsWinUtils::IsWindowEmulationStarted()) {
565 // The embedded document should use the same emulated window handle as
566 // its embedder. It will return the embedder document (not a window
567 // accessible) as the parent accessible, so we pass a null accessible
568 // when sending the window to the embedded document.
569 aChildDoc->SetEmulatedWindowHandle(mEmulatedWindowHandle);
570 Unused << aChildDoc->SendEmulatedWindow(
571 reinterpret_cast<uintptr_t>(mEmulatedWindowHandle), nullptr);
574 #endif // defined(XP_WIN)
576 return IPC_OK();
579 mozilla::ipc::IPCResult DocAccessibleParent::RecvShutdown() {
580 Destroy();
582 auto mgr = static_cast<dom::BrowserParent*>(Manager());
583 if (!mgr->IsDestroyed()) {
584 if (!PDocAccessibleParent::Send__delete__(this)) {
585 return IPC_FAIL_NO_REASON(mgr);
589 return IPC_OK();
592 void DocAccessibleParent::Destroy() {
593 // If we are already shutdown that is because our containing tab parent is
594 // shutting down in which case we don't need to do anything.
595 if (mShutdown) {
596 return;
599 mShutdown = true;
601 MOZ_DIAGNOSTIC_ASSERT(LiveDocs().Contains(mActorID));
602 uint32_t childDocCount = mChildDocs.Length();
603 for (uint32_t i = 0; i < childDocCount; i++) {
604 for (uint32_t j = i + 1; j < childDocCount; j++) {
605 MOZ_DIAGNOSTIC_ASSERT(mChildDocs[i] != mChildDocs[j]);
609 // XXX This indirection through the hash map of live documents shouldn't be
610 // needed, but be paranoid for now.
611 int32_t actorID = mActorID;
612 for (uint32_t i = childDocCount - 1; i < childDocCount; i--) {
613 DocAccessibleParent* thisDoc = LiveDocs().Get(actorID);
614 MOZ_ASSERT(thisDoc);
615 if (!thisDoc) {
616 return;
619 thisDoc->ChildDocAt(i)->Destroy();
622 for (auto iter = mAccessibles.Iter(); !iter.Done(); iter.Next()) {
623 MOZ_ASSERT(iter.Get()->mProxy != this);
624 ProxyDestroyed(iter.Get()->mProxy);
625 iter.Remove();
628 DocAccessibleParent* thisDoc = LiveDocs().Get(actorID);
629 MOZ_ASSERT(thisDoc);
630 if (!thisDoc) {
631 return;
634 // The code above should have already completely cleared these, but to be
635 // extra safe make sure they are cleared here.
636 thisDoc->mAccessibles.Clear();
637 thisDoc->mChildDocs.Clear();
639 DocManager::NotifyOfRemoteDocShutdown(thisDoc);
640 thisDoc = LiveDocs().Get(actorID);
641 MOZ_ASSERT(thisDoc);
642 if (!thisDoc) {
643 return;
646 ProxyDestroyed(thisDoc);
647 thisDoc = LiveDocs().Get(actorID);
648 MOZ_ASSERT(thisDoc);
649 if (!thisDoc) {
650 return;
653 if (DocAccessibleParent* parentDoc = thisDoc->ParentDoc())
654 parentDoc->RemoveChildDoc(thisDoc);
655 else if (IsTopLevel())
656 GetAccService()->RemoteDocShutdown(this);
659 DocAccessibleParent* DocAccessibleParent::ParentDoc() const {
660 if (mParentDoc == kNoParentDoc) {
661 return nullptr;
664 return LiveDocs().Get(mParentDoc);
667 bool DocAccessibleParent::CheckDocTree() const {
668 size_t childDocs = mChildDocs.Length();
669 for (size_t i = 0; i < childDocs; i++) {
670 const DocAccessibleParent* childDoc = ChildDocAt(i);
671 if (!childDoc || childDoc->ParentDoc() != this) return false;
673 if (!childDoc->CheckDocTree()) {
674 return false;
678 return true;
681 xpcAccessibleGeneric* DocAccessibleParent::GetXPCAccessible(
682 ProxyAccessible* aProxy) {
683 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
684 MOZ_ASSERT(doc);
686 return doc->GetXPCAccessible(aProxy);
689 #if defined(XP_WIN)
690 void DocAccessibleParent::MaybeInitWindowEmulation() {
691 if (!nsWinUtils::IsWindowEmulationStarted()) {
692 return;
695 // XXX get the bounds from the browserParent instead of poking at accessibles
696 // which might not exist yet.
697 Accessible* outerDoc = OuterDocOfRemoteBrowser();
698 if (!outerDoc) {
699 return;
702 RootAccessible* rootDocument = outerDoc->RootAccessible();
703 MOZ_ASSERT(rootDocument);
705 bool isActive = true;
706 nsIntRect rect(CW_USEDEFAULT, CW_USEDEFAULT, 0, 0);
707 if (Compatibility::IsDolphin()) {
708 rect = Bounds();
709 nsIntRect rootRect = rootDocument->Bounds();
710 rect.MoveToX(rootRect.X() - rect.X());
711 rect.MoveToY(rect.Y() - rootRect.Y());
713 auto browserParent = static_cast<dom::BrowserParent*>(Manager());
714 isActive = browserParent->GetDocShellIsActive();
717 // onCreate is guaranteed to be called synchronously by
718 // nsWinUtils::CreateNativeWindow, so this reference isn't really necessary.
719 // However, static analysis complains without it.
720 RefPtr<DocAccessibleParent> thisRef = this;
721 nsWinUtils::NativeWindowCreateProc onCreate([thisRef](HWND aHwnd) -> void {
722 IDispatchHolder hWndAccHolder;
724 ::SetPropW(aHwnd, kPropNameDocAccParent,
725 reinterpret_cast<HANDLE>(thisRef.get()));
727 thisRef->SetEmulatedWindowHandle(aHwnd);
729 RefPtr<IAccessible> hwndAcc;
730 if (SUCCEEDED(::AccessibleObjectFromWindow(
731 aHwnd, OBJID_WINDOW, IID_IAccessible, getter_AddRefs(hwndAcc)))) {
732 RefPtr<IDispatch> wrapped(
733 mscom::PassthruProxy::Wrap<IDispatch>(WrapNotNull(hwndAcc)));
734 hWndAccHolder.Set(IDispatchHolder::COMPtrType(
735 mscom::ToProxyUniquePtr(std::move(wrapped))));
738 Unused << thisRef->SendEmulatedWindow(
739 reinterpret_cast<uintptr_t>(thisRef->mEmulatedWindowHandle),
740 hWndAccHolder);
743 HWND parentWnd = reinterpret_cast<HWND>(rootDocument->GetNativeWindow());
744 DebugOnly<HWND> hWnd = nsWinUtils::CreateNativeWindow(
745 kClassNameTabContent, parentWnd, rect.X(), rect.Y(), rect.Width(),
746 rect.Height(), isActive, &onCreate);
747 MOZ_ASSERT(hWnd);
750 void DocAccessibleParent::SendParentCOMProxy(Accessible* aOuterDoc) {
751 // Make sure that we're not racing with a tab shutdown
752 auto tab = static_cast<dom::BrowserParent*>(Manager());
753 MOZ_ASSERT(tab);
754 if (tab->IsDestroyed()) {
755 return;
758 RefPtr<IAccessible> nativeAcc;
759 aOuterDoc->GetNativeInterface(getter_AddRefs(nativeAcc));
760 MOZ_ASSERT(nativeAcc);
762 RefPtr<IDispatch> wrapped(
763 mscom::PassthruProxy::Wrap<IDispatch>(WrapNotNull(nativeAcc)));
765 IDispatchHolder::COMPtrType ptr(mscom::ToProxyUniquePtr(std::move(wrapped)));
766 IDispatchHolder holder(std::move(ptr));
767 if (!PDocAccessibleParent::SendParentCOMProxy(holder)) {
768 return;
771 # if defined(MOZ_SANDBOX)
772 mParentProxyStream = holder.GetPreservedStream();
773 # endif // defined(MOZ_SANDBOX)
776 void DocAccessibleParent::SetEmulatedWindowHandle(HWND aWindowHandle) {
777 if (!aWindowHandle && mEmulatedWindowHandle && IsTopLevel()) {
778 ::DestroyWindow(mEmulatedWindowHandle);
780 mEmulatedWindowHandle = aWindowHandle;
783 mozilla::ipc::IPCResult DocAccessibleParent::RecvGetWindowedPluginIAccessible(
784 const WindowsHandle& aHwnd, IAccessibleHolder* aPluginCOMProxy) {
785 # if defined(MOZ_SANDBOX)
786 // We don't actually want the accessible object for aHwnd, but rather the
787 // one that belongs to its child (see HTMLWin32ObjectAccessible).
788 HWND childWnd = ::GetWindow(reinterpret_cast<HWND>(aHwnd), GW_CHILD);
789 if (!childWnd) {
790 // We're seeing this in the wild - the plugin is windowed but we no longer
791 // have a window.
792 return IPC_OK();
795 IAccessible* rawAccPlugin = nullptr;
796 HRESULT hr = ::AccessibleObjectFromWindow(
797 childWnd, OBJID_WINDOW, IID_IAccessible, (void**)&rawAccPlugin);
798 if (FAILED(hr)) {
799 // This might happen if the plugin doesn't handle WM_GETOBJECT properly.
800 // We should not consider that a failure.
801 return IPC_OK();
804 aPluginCOMProxy->Set(IAccessibleHolder::COMPtrType(rawAccPlugin));
806 return IPC_OK();
807 # else
808 return IPC_FAIL(this, "Message unsupported in this build configuration");
809 # endif
812 mozilla::ipc::IPCResult DocAccessibleParent::RecvFocusEvent(
813 const uint64_t& aID, const LayoutDeviceIntRect& aCaretRect) {
814 if (mShutdown) {
815 return IPC_OK();
818 ProxyAccessible* proxy = GetAccessible(aID);
819 if (!proxy) {
820 NS_ERROR("no proxy for event!");
821 return IPC_OK();
824 ProxyFocusEvent(proxy, aCaretRect);
826 if (!nsCoreUtils::AccEventObserversExist()) {
827 return IPC_OK();
830 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
831 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
832 nsINode* node = nullptr;
833 bool fromUser = true; // XXX fix me
834 RefPtr<xpcAccEvent> event = new xpcAccEvent(nsIAccessibleEvent::EVENT_FOCUS,
835 xpcAcc, doc, node, fromUser);
836 nsCoreUtils::DispatchAccEvent(std::move(event));
838 return IPC_OK();
841 #endif // defined(XP_WIN)
843 #if !defined(XP_WIN)
844 mozilla::ipc::IPCResult DocAccessibleParent::RecvBatch(
845 const uint64_t& aBatchType, nsTArray<BatchData>&& aData) {
846 // Only do something in Android. We can't ifdef the entire protocol out in
847 // the ipdl because it doesn't allow preprocessing.
848 # if defined(ANDROID)
849 nsTArray<ProxyAccessible*> proxies(aData.Length());
850 for (size_t i = 0; i < aData.Length(); i++) {
851 DocAccessibleParent* doc = static_cast<DocAccessibleParent*>(
852 aData.ElementAt(i).Document().get_PDocAccessibleParent());
853 MOZ_ASSERT(doc);
855 ProxyAccessible* proxy = doc->GetAccessible(aData.ElementAt(i).ID());
856 if (!proxy) {
857 MOZ_ASSERT_UNREACHABLE("No proxy found!");
858 continue;
861 proxies.AppendElement(proxy);
863 ProxyBatch(this, aBatchType, proxies, aData);
864 # endif // defined(XP_WIN)
865 return IPC_OK();
867 #endif // !defined(XP_WIN)
869 Tuple<DocAccessibleParent*, uint64_t> DocAccessibleParent::GetRemoteEmbedder() {
870 dom::BrowserParent* embeddedBrowser = dom::BrowserParent::GetFrom(Manager());
871 dom::BrowserBridgeParent* bridge = embeddedBrowser->GetBrowserBridgeParent();
872 if (!bridge) {
873 return Tuple<DocAccessibleParent*, uint64_t>(nullptr, 0);
875 DocAccessibleParent* doc;
876 uint64_t id;
877 Tie(doc, id) = bridge->GetEmbedderAccessible();
878 if (doc && doc->IsShutdown()) {
879 // Sometimes, the embedder document is destroyed before its
880 // BrowserBridgeParent. Don't return a destroyed document.
881 doc = nullptr;
882 id = 0;
884 return Tuple<DocAccessibleParent*, uint64_t>(doc, id);
887 } // namespace a11y
888 } // namespace mozilla