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 "TextRange.h"
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"
24 # include "mozilla/a11y/DocAccessiblePlatformExtParent.h"
32 // Needed by mscom::PassthruProxy::Wrap<IAccessible>.
34 struct VTableSizer
<IAccessible
> {
35 // 3 methods in IUnknown + 4 in IDispatch + 21 in IAccessible = 28 total
40 #endif // defined (XP_WIN)
43 uint64_t DocAccessibleParent::sMaxDocID
= 0;
45 mozilla::ipc::IPCResult
DocAccessibleParent::RecvShowEvent(
46 const ShowEventData
& aData
, const bool& aFromUser
) {
47 if (mShutdown
) return IPC_OK();
49 MOZ_ASSERT(CheckDocTree());
51 if (aData
.NewTree().IsEmpty()) {
52 return IPC_FAIL(this, "No children being added");
55 RemoteAccessible
* parent
= GetAccessible(aData
.ID());
57 // XXX This should really never happen, but sometimes we fail to fire the
58 // required show events.
60 NS_ERROR("adding child to unknown accessible");
62 return IPC_FAIL(this, "unknown parent accessible");
68 uint32_t newChildIdx
= aData
.Idx();
69 if (newChildIdx
> parent
->ChildCount()) {
70 NS_ERROR("invalid index to add child at");
72 return IPC_FAIL(this, "invalid index");
78 uint32_t consumed
= AddSubtree(parent
, aData
.NewTree(), 0, newChildIdx
);
79 MOZ_ASSERT(consumed
== aData
.NewTree().Length());
81 // XXX This shouldn't happen, but if we failed to add children then the below
82 // is pointless and can crash.
84 return IPC_FAIL(this, "failed to add children");
88 for (uint32_t i
= 0; i
< consumed
; i
++) {
89 uint64_t id
= aData
.NewTree()[i
].ID();
90 MOZ_ASSERT(mAccessibles
.GetEntry(id
));
94 MOZ_ASSERT(CheckDocTree());
96 // Just update, no events.
97 if (aData
.EventSuppressed()) {
101 RemoteAccessible
* target
= parent
->RemoteChildAt(newChildIdx
);
102 ProxyShowHideEvent(target
, parent
, true, aFromUser
);
104 if (!nsCoreUtils::AccEventObserversExist()) {
108 uint32_t type
= nsIAccessibleEvent::EVENT_SHOW
;
109 xpcAccessibleGeneric
* xpcAcc
= GetXPCAccessible(target
);
110 xpcAccessibleDocument
* doc
= GetAccService()->GetXPCDocument(this);
111 nsINode
* node
= nullptr;
112 RefPtr
<xpcAccEvent
> event
=
113 new xpcAccEvent(type
, xpcAcc
, doc
, node
, aFromUser
);
114 nsCoreUtils::DispatchAccEvent(std::move(event
));
119 uint32_t DocAccessibleParent::AddSubtree(
120 RemoteAccessible
* aParent
, const nsTArray
<a11y::AccessibleData
>& aNewTree
,
121 uint32_t aIdx
, uint32_t aIdxInParent
) {
122 if (aNewTree
.Length() <= aIdx
) {
123 NS_ERROR("bad index in serialized tree!");
127 const AccessibleData
& newChild
= aNewTree
[aIdx
];
129 if (mAccessibles
.Contains(newChild
.ID())) {
130 NS_ERROR("ID already in use");
134 RemoteAccessible
* newProxy
= new RemoteAccessible(
135 newChild
.ID(), aParent
, this, newChild
.Role(), newChild
.Type(),
136 newChild
.GenericTypes(), newChild
.RoleMapEntryIndex());
138 aParent
->AddChildAt(aIdxInParent
, newProxy
);
139 mAccessibles
.PutEntry(newChild
.ID())->mProxy
= newProxy
;
140 ProxyCreated(newProxy
);
143 WrapperFor(newProxy
)->GetMsaa()->SetID(newChild
.MsaaID());
146 for (uint32_t index
= 0, len
= mPendingChildDocs
.Length(); index
< len
;
148 PendingChildDoc
& pending
= mPendingChildDocs
[index
];
149 if (pending
.mParentID
== newChild
.ID()) {
150 if (!pending
.mChildDoc
->IsShutdown()) {
151 AddChildDoc(pending
.mChildDoc
, pending
.mParentID
, false);
153 mPendingChildDocs
.RemoveElementAt(index
);
157 DebugOnly
<bool> isOuterDoc
= newProxy
->ChildCount() == 1;
159 uint32_t accessibles
= 1;
160 uint32_t kids
= newChild
.ChildrenCount();
161 for (uint32_t i
= 0; i
< kids
; i
++) {
162 uint32_t consumed
= AddSubtree(newProxy
, aNewTree
, aIdx
+ accessibles
, i
);
163 if (!consumed
) return 0;
165 accessibles
+= consumed
;
168 MOZ_ASSERT((isOuterDoc
&& kids
== 0) || newProxy
->ChildCount() == kids
);
173 mozilla::ipc::IPCResult
DocAccessibleParent::RecvHideEvent(
174 const uint64_t& aRootID
, const bool& aFromUser
) {
175 if (mShutdown
) return IPC_OK();
177 MOZ_ASSERT(CheckDocTree());
179 // We shouldn't actually need this because mAccessibles shouldn't have an
180 // entry for the document itself, but it doesn't hurt to be explicit.
182 return IPC_FAIL(this, "Trying to hide entire document?");
185 ProxyEntry
* rootEntry
= mAccessibles
.GetEntry(aRootID
);
187 NS_ERROR("invalid root being removed!");
191 RemoteAccessible
* root
= rootEntry
->mProxy
;
193 NS_ERROR("invalid root being removed!");
197 RemoteAccessible
* parent
= root
->RemoteParent();
198 ProxyShowHideEvent(root
, parent
, false, aFromUser
);
200 RefPtr
<xpcAccHideEvent
> event
= nullptr;
201 if (nsCoreUtils::AccEventObserversExist()) {
202 uint32_t type
= nsIAccessibleEvent::EVENT_HIDE
;
203 xpcAccessibleGeneric
* xpcAcc
= GetXPCAccessible(root
);
204 xpcAccessibleGeneric
* xpcParent
= GetXPCAccessible(parent
);
205 RemoteAccessible
* next
= root
->RemoteNextSibling();
206 xpcAccessibleGeneric
* xpcNext
= next
? GetXPCAccessible(next
) : nullptr;
207 RemoteAccessible
* prev
= root
->RemotePrevSibling();
208 xpcAccessibleGeneric
* xpcPrev
= prev
? GetXPCAccessible(prev
) : nullptr;
209 xpcAccessibleDocument
* doc
= GetAccService()->GetXPCDocument(this);
210 nsINode
* node
= nullptr;
211 event
= new xpcAccHideEvent(type
, xpcAcc
, doc
, node
, aFromUser
, xpcParent
,
215 parent
->RemoveChild(root
);
218 MOZ_ASSERT(CheckDocTree());
221 nsCoreUtils::DispatchAccEvent(std::move(event
));
227 mozilla::ipc::IPCResult
DocAccessibleParent::RecvEvent(
228 const uint64_t& aID
, const uint32_t& aEventType
) {
233 RemoteAccessible
* proxy
= GetAccessible(aID
);
235 NS_ERROR("no proxy for event!");
239 ProxyEvent(proxy
, aEventType
);
241 if (!nsCoreUtils::AccEventObserversExist()) {
245 xpcAccessibleGeneric
* xpcAcc
= GetXPCAccessible(proxy
);
246 xpcAccessibleDocument
* doc
= GetAccService()->GetXPCDocument(this);
247 nsINode
* node
= nullptr;
248 bool fromUser
= true; // XXX fix me
249 RefPtr
<xpcAccEvent
> event
=
250 new xpcAccEvent(aEventType
, xpcAcc
, doc
, node
, fromUser
);
251 nsCoreUtils::DispatchAccEvent(std::move(event
));
256 mozilla::ipc::IPCResult
DocAccessibleParent::RecvStateChangeEvent(
257 const uint64_t& aID
, const uint64_t& aState
, const bool& aEnabled
) {
262 RemoteAccessible
* target
= GetAccessible(aID
);
264 NS_ERROR("we don't know about the target of a state change event!");
268 ProxyStateChangeEvent(target
, aState
, aEnabled
);
270 if (!nsCoreUtils::AccEventObserversExist()) {
274 xpcAccessibleGeneric
* xpcAcc
= GetXPCAccessible(target
);
275 xpcAccessibleDocument
* doc
= GetAccService()->GetXPCDocument(this);
276 uint32_t type
= nsIAccessibleEvent::EVENT_STATE_CHANGE
;
278 uint32_t state
= nsAccUtils::To32States(aState
, &extra
);
279 bool fromUser
= true; // XXX fix this
280 nsINode
* node
= nullptr; // XXX can we do better?
281 RefPtr
<xpcAccStateChangeEvent
> event
= new xpcAccStateChangeEvent(
282 type
, xpcAcc
, doc
, node
, fromUser
, state
, extra
, aEnabled
);
283 nsCoreUtils::DispatchAccEvent(std::move(event
));
288 mozilla::ipc::IPCResult
DocAccessibleParent::RecvCaretMoveEvent(
291 const LayoutDeviceIntRect
& aCaretRect
,
292 #endif // defined (XP_WIN)
293 const int32_t& aOffset
, const bool& aIsSelectionCollapsed
) {
298 RemoteAccessible
* proxy
= GetAccessible(aID
);
300 NS_ERROR("unknown caret move event target!");
305 ProxyCaretMoveEvent(proxy
, aCaretRect
);
307 ProxyCaretMoveEvent(proxy
, aOffset
, aIsSelectionCollapsed
);
310 if (!nsCoreUtils::AccEventObserversExist()) {
314 xpcAccessibleGeneric
* xpcAcc
= GetXPCAccessible(proxy
);
315 xpcAccessibleDocument
* doc
= GetAccService()->GetXPCDocument(this);
316 nsINode
* node
= nullptr;
317 bool fromUser
= true; // XXX fix me
318 uint32_t type
= nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED
;
319 RefPtr
<xpcAccCaretMoveEvent
> event
= new xpcAccCaretMoveEvent(
320 type
, xpcAcc
, doc
, node
, fromUser
, aOffset
, aIsSelectionCollapsed
);
321 nsCoreUtils::DispatchAccEvent(std::move(event
));
326 mozilla::ipc::IPCResult
DocAccessibleParent::RecvTextChangeEvent(
327 const uint64_t& aID
, const nsString
& aStr
, const int32_t& aStart
,
328 const uint32_t& aLen
, const bool& aIsInsert
, const bool& aFromUser
) {
333 RemoteAccessible
* target
= GetAccessible(aID
);
335 NS_ERROR("text change event target is unknown!");
339 ProxyTextChangeEvent(target
, aStr
, aStart
, aLen
, aIsInsert
, aFromUser
);
341 if (!nsCoreUtils::AccEventObserversExist()) {
345 xpcAccessibleGeneric
* xpcAcc
= GetXPCAccessible(target
);
346 xpcAccessibleDocument
* doc
= GetAccService()->GetXPCDocument(this);
347 uint32_t type
= aIsInsert
? nsIAccessibleEvent::EVENT_TEXT_INSERTED
348 : nsIAccessibleEvent::EVENT_TEXT_REMOVED
;
349 nsINode
* node
= nullptr;
350 RefPtr
<xpcAccTextChangeEvent
> event
= new xpcAccTextChangeEvent(
351 type
, xpcAcc
, doc
, node
, aFromUser
, aStart
, aLen
, aIsInsert
, aStr
);
352 nsCoreUtils::DispatchAccEvent(std::move(event
));
359 mozilla::ipc::IPCResult
DocAccessibleParent::RecvSyncTextChangeEvent(
360 const uint64_t& aID
, const nsString
& aStr
, const int32_t& aStart
,
361 const uint32_t& aLen
, const bool& aIsInsert
, const bool& aFromUser
) {
362 return RecvTextChangeEvent(aID
, aStr
, aStart
, aLen
, aIsInsert
, aFromUser
);
365 #endif // defined(XP_WIN)
367 mozilla::ipc::IPCResult
DocAccessibleParent::RecvSelectionEvent(
368 const uint64_t& aID
, const uint64_t& aWidgetID
, const uint32_t& aType
) {
373 RemoteAccessible
* target
= GetAccessible(aID
);
374 RemoteAccessible
* widget
= GetAccessible(aWidgetID
);
375 if (!target
|| !widget
) {
376 NS_ERROR("invalid id in selection event");
380 ProxySelectionEvent(target
, widget
, aType
);
381 if (!nsCoreUtils::AccEventObserversExist()) {
384 xpcAccessibleGeneric
* xpcTarget
= GetXPCAccessible(target
);
385 xpcAccessibleDocument
* xpcDoc
= GetAccService()->GetXPCDocument(this);
386 RefPtr
<xpcAccEvent
> event
=
387 new xpcAccEvent(aType
, xpcTarget
, xpcDoc
, nullptr, false);
388 nsCoreUtils::DispatchAccEvent(std::move(event
));
393 mozilla::ipc::IPCResult
DocAccessibleParent::RecvVirtualCursorChangeEvent(
394 const uint64_t& aID
, const uint64_t& aOldPositionID
,
395 const int32_t& aOldStartOffset
, const int32_t& aOldEndOffset
,
396 const uint64_t& aNewPositionID
, const int32_t& aNewStartOffset
,
397 const int32_t& aNewEndOffset
, const int16_t& aReason
,
398 const int16_t& aBoundaryType
, const bool& aFromUser
) {
403 RemoteAccessible
* target
= GetAccessible(aID
);
404 RemoteAccessible
* oldPosition
= GetAccessible(aOldPositionID
);
405 RemoteAccessible
* newPosition
= GetAccessible(aNewPositionID
);
408 NS_ERROR("no proxy for event!");
413 ProxyVirtualCursorChangeEvent(
414 target
, oldPosition
, aOldStartOffset
, aOldEndOffset
, newPosition
,
415 aNewStartOffset
, aNewEndOffset
, aReason
, aBoundaryType
, aFromUser
);
418 if (!nsCoreUtils::AccEventObserversExist()) {
422 xpcAccessibleDocument
* doc
= GetAccService()->GetXPCDocument(this);
423 RefPtr
<xpcAccVirtualCursorChangeEvent
> event
=
424 new xpcAccVirtualCursorChangeEvent(
425 nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED
,
426 GetXPCAccessible(target
), doc
, nullptr, aFromUser
,
427 GetXPCAccessible(oldPosition
), aOldStartOffset
, aOldEndOffset
,
428 GetXPCAccessible(newPosition
), aNewStartOffset
, aNewEndOffset
,
429 aBoundaryType
, aReason
);
430 nsCoreUtils::DispatchAccEvent(std::move(event
));
435 mozilla::ipc::IPCResult
DocAccessibleParent::RecvScrollingEvent(
436 const uint64_t& aID
, const uint64_t& aType
, const uint32_t& aScrollX
,
437 const uint32_t& aScrollY
, const uint32_t& aMaxScrollX
,
438 const uint32_t& aMaxScrollY
) {
443 RemoteAccessible
* target
= GetAccessible(aID
);
445 NS_ERROR("no proxy for event!");
450 ProxyScrollingEvent(target
, aType
, aScrollX
, aScrollY
, aMaxScrollX
,
453 ProxyEvent(target
, aType
);
456 if (!nsCoreUtils::AccEventObserversExist()) {
460 xpcAccessibleGeneric
* xpcAcc
= GetXPCAccessible(target
);
461 xpcAccessibleDocument
* doc
= GetAccService()->GetXPCDocument(this);
462 nsINode
* node
= nullptr;
463 bool fromUser
= true; // XXX: Determine if this was from user input.
464 RefPtr
<xpcAccScrollingEvent
> event
=
465 new xpcAccScrollingEvent(aType
, xpcAcc
, doc
, node
, fromUser
, aScrollX
,
466 aScrollY
, aMaxScrollX
, aMaxScrollY
);
467 nsCoreUtils::DispatchAccEvent(std::move(event
));
473 mozilla::ipc::IPCResult
DocAccessibleParent::RecvAnnouncementEvent(
474 const uint64_t& aID
, const nsString
& aAnnouncement
,
475 const uint16_t& aPriority
) {
480 RemoteAccessible
* target
= GetAccessible(aID
);
482 NS_ERROR("no proxy for event!");
486 # if defined(ANDROID)
487 ProxyAnnouncementEvent(target
, aAnnouncement
, aPriority
);
490 if (!nsCoreUtils::AccEventObserversExist()) {
494 xpcAccessibleGeneric
* xpcAcc
= GetXPCAccessible(target
);
495 xpcAccessibleDocument
* doc
= GetAccService()->GetXPCDocument(this);
496 RefPtr
<xpcAccAnnouncementEvent
> event
= new xpcAccAnnouncementEvent(
497 nsIAccessibleEvent::EVENT_ANNOUNCEMENT
, xpcAcc
, doc
, nullptr, false,
498 aAnnouncement
, aPriority
);
499 nsCoreUtils::DispatchAccEvent(std::move(event
));
504 mozilla::ipc::IPCResult
DocAccessibleParent::RecvTextSelectionChangeEvent(
505 const uint64_t& aID
, nsTArray
<TextRangeData
>&& aSelection
) {
506 # ifdef MOZ_WIDGET_COCOA
511 RemoteAccessible
* target
= GetAccessible(aID
);
513 NS_ERROR("no proxy for event!");
517 ProxyTextSelectionChangeEvent(target
, aSelection
);
521 return RecvEvent(aID
, nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED
);
527 mozilla::ipc::IPCResult
DocAccessibleParent::RecvRoleChangedEvent(
528 const a11y::role
& aRole
) {
535 #ifdef MOZ_WIDGET_COCOA
536 ProxyRoleChangedEvent(this, aRole
);
542 mozilla::ipc::IPCResult
DocAccessibleParent::RecvBindChildDoc(
543 PDocAccessibleParent
* aChildDoc
, const uint64_t& aID
) {
544 // One document should never directly be the child of another.
545 // We should always have at least an outer doc accessible in between.
547 if (!aID
) return IPC_FAIL(this, "ID is 0!");
553 MOZ_ASSERT(CheckDocTree());
555 auto childDoc
= static_cast<DocAccessibleParent
*>(aChildDoc
);
557 ipc::IPCResult result
= AddChildDoc(childDoc
, aID
, false);
559 MOZ_ASSERT(CheckDocTree());
571 ipc::IPCResult
DocAccessibleParent::AddChildDoc(DocAccessibleParent
* aChildDoc
,
574 // We do not use GetAccessible here because we want to be sure to not get the
576 ProxyEntry
* e
= mAccessibles
.GetEntry(aParentID
);
578 if (aChildDoc
->IsTopLevelInContentProcess()) {
579 // aChildDoc is an embedded document in a different content process to
580 // this document. Sometimes, AddChildDoc gets called before the embedder
581 // sends us the OuterDocAccessible. We must add the child when the
582 // OuterDocAccessible proxy gets created later.
584 for (uint32_t index
= 0, len
= mPendingChildDocs
.Length(); index
< len
;
586 MOZ_ASSERT(mPendingChildDocs
[index
].mChildDoc
!= aChildDoc
,
587 "Child doc already pending addition!");
590 mPendingChildDocs
.AppendElement(PendingChildDoc(aChildDoc
, aParentID
));
592 ProxyCreated(aChildDoc
);
596 MOZ_DIAGNOSTIC_ASSERT(false, "Binding to nonexistent proxy!");
597 return IPC_FAIL(this, "binding to nonexistant proxy!");
600 RemoteAccessible
* outerDoc
= e
->mProxy
;
601 MOZ_ASSERT(outerDoc
);
603 // OuterDocAccessibles are expected to only have a document as a child.
604 // However for compatibility we tolerate replacing one document with another
606 if (!outerDoc
->IsOuterDoc() || outerDoc
->ChildCount() > 1 ||
607 (outerDoc
->ChildCount() == 1 && !outerDoc
->RemoteChildAt(0)->IsDoc())) {
608 MOZ_DIAGNOSTIC_ASSERT(false,
609 "Binding to parent that isn't a valid OuterDoc!");
610 return IPC_FAIL(this, "Binding to parent that isn't a valid OuterDoc!");
613 if (outerDoc
->ChildCount() == 1) {
614 MOZ_ASSERT(outerDoc
->RemoteChildAt(0)->AsDoc());
615 outerDoc
->RemoteChildAt(0)->AsDoc()->Unbind();
618 aChildDoc
->SetParent(outerDoc
);
619 outerDoc
->SetChildDoc(aChildDoc
);
620 mChildDocs
.AppendElement(aChildDoc
->mActorID
);
621 aChildDoc
->mParentDoc
= mActorID
;
624 ProxyCreated(aChildDoc
);
627 if (aChildDoc
->IsTopLevelInContentProcess()) {
628 // aChildDoc is an embedded document in a different content process to
630 auto embeddedBrowser
=
631 static_cast<dom::BrowserParent
*>(aChildDoc
->Manager());
632 dom::BrowserBridgeParent
* bridge
=
633 embeddedBrowser
->GetBrowserBridgeParent();
636 // Send a COM proxy for the embedded document to the embedder process
637 // hosting the iframe. This will be returned as the child of the
638 // embedder OuterDocAccessible.
639 RefPtr
<IDispatch
> docAcc
;
640 aChildDoc
->GetCOMInterface((void**)getter_AddRefs(docAcc
));
643 RefPtr
<IDispatch
> docWrapped(
644 mscom::PassthruProxy::Wrap
<IDispatch
>(WrapNotNull(docAcc
)));
645 IDispatchHolder::COMPtrType
docPtr(
646 mscom::ToProxyUniquePtr(std::move(docWrapped
)));
647 IDispatchHolder
docHolder(std::move(docPtr
));
648 if (bridge
->SendSetEmbeddedDocAccessibleCOMProxy(docHolder
)) {
649 # if defined(MOZ_SANDBOX)
650 aChildDoc
->mDocProxyStream
= docHolder
.GetPreservedStream();
651 # endif // defined(MOZ_SANDBOX)
654 // Send a COM proxy for the embedder OuterDocAccessible to the embedded
655 // document process. This will be returned as the parent of the
656 // embedded document.
657 aChildDoc
->SendParentCOMProxy(WrapperFor(outerDoc
));
658 if (nsWinUtils::IsWindowEmulationStarted()) {
659 // The embedded document should use the same emulated window handle as
660 // its embedder. It will return the embedder document (not a window
661 // accessible) as the parent accessible, so we pass a null accessible
662 // when sending the window to the embedded document.
663 aChildDoc
->SetEmulatedWindowHandle(mEmulatedWindowHandle
);
664 Unused
<< aChildDoc
->SendEmulatedWindow(
665 reinterpret_cast<uintptr_t>(mEmulatedWindowHandle
), nullptr);
667 // Send a COM proxy for the top level document to the embedded document
668 // process. This will be returned when the client calls QueryService
669 // with SID_IAccessibleContentDocument on an accessible in the embedded
671 DocAccessibleParent
* topDoc
= this;
672 while (DocAccessibleParent
* parentDoc
= topDoc
->ParentDoc()) {
675 MOZ_ASSERT(topDoc
&& topDoc
->IsTopLevel());
676 RefPtr
<IAccessible
> topDocAcc
;
677 topDoc
->GetCOMInterface((void**)getter_AddRefs(topDocAcc
));
678 MOZ_ASSERT(topDocAcc
);
680 RefPtr
<IAccessible
> topDocWrapped(
681 mscom::PassthruProxy::Wrap
<IAccessible
>(WrapNotNull(topDocAcc
)));
682 IAccessibleHolder::COMPtrType
topDocPtr(
683 mscom::ToProxyUniquePtr(std::move(topDocWrapped
)));
684 IAccessibleHolder
topDocHolder(std::move(topDocPtr
));
685 if (aChildDoc
->SendTopLevelDocCOMProxy(topDocHolder
)) {
686 # if defined(MOZ_SANDBOX)
687 aChildDoc
->mTopLevelDocProxyStream
=
688 topDocHolder
.GetPreservedStream();
689 # endif // defined(MOZ_SANDBOX)
692 #endif // defined(XP_WIN)
693 // We need to fire a reorder event on the outer doc accessible.
694 // For same-process documents, this is fired by the content process, but
695 // this isn't possible when the document is in a different process to its
697 // RecvEvent fires both OS and XPCOM events.
698 Unused
<< RecvEvent(aParentID
, nsIAccessibleEvent::EVENT_REORDER
);
705 mozilla::ipc::IPCResult
DocAccessibleParent::RecvShutdown() {
708 auto mgr
= static_cast<dom::BrowserParent
*>(Manager());
709 if (!mgr
->IsDestroyed()) {
710 if (!PDocAccessibleParent::Send__delete__(this)) {
711 return IPC_FAIL_NO_REASON(mgr
);
718 void DocAccessibleParent::Destroy() {
719 // If we are already shutdown that is because our containing tab parent is
720 // shutting down in which case we don't need to do anything.
727 MOZ_DIAGNOSTIC_ASSERT(LiveDocs().Contains(mActorID
));
728 uint32_t childDocCount
= mChildDocs
.Length();
729 for (uint32_t i
= 0; i
< childDocCount
; i
++) {
730 for (uint32_t j
= i
+ 1; j
< childDocCount
; j
++) {
731 MOZ_DIAGNOSTIC_ASSERT(mChildDocs
[i
] != mChildDocs
[j
]);
735 // XXX This indirection through the hash map of live documents shouldn't be
736 // needed, but be paranoid for now.
737 int32_t actorID
= mActorID
;
738 for (uint32_t i
= childDocCount
- 1; i
< childDocCount
; i
--) {
739 DocAccessibleParent
* thisDoc
= LiveDocs().Get(actorID
);
745 thisDoc
->ChildDocAt(i
)->Destroy();
748 for (auto iter
= mAccessibles
.Iter(); !iter
.Done(); iter
.Next()) {
749 MOZ_ASSERT(iter
.Get()->mProxy
!= this);
750 ProxyDestroyed(iter
.Get()->mProxy
);
754 DocAccessibleParent
* thisDoc
= LiveDocs().Get(actorID
);
760 // The code above should have already completely cleared these, but to be
761 // extra safe make sure they are cleared here.
762 thisDoc
->mAccessibles
.Clear();
763 thisDoc
->mChildDocs
.Clear();
765 DocManager::NotifyOfRemoteDocShutdown(thisDoc
);
766 thisDoc
= LiveDocs().Get(actorID
);
772 ProxyDestroyed(thisDoc
);
773 thisDoc
= LiveDocs().Get(actorID
);
779 if (DocAccessibleParent
* parentDoc
= thisDoc
->ParentDoc()) {
780 parentDoc
->RemoveChildDoc(thisDoc
);
781 } else if (IsTopLevel()) {
782 GetAccService()->RemoteDocShutdown(this);
786 DocAccessibleParent
* DocAccessibleParent::ParentDoc() const {
787 if (mParentDoc
== kNoParentDoc
) {
791 return LiveDocs().Get(mParentDoc
);
794 bool DocAccessibleParent::CheckDocTree() const {
795 size_t childDocs
= mChildDocs
.Length();
796 for (size_t i
= 0; i
< childDocs
; i
++) {
797 const DocAccessibleParent
* childDoc
= ChildDocAt(i
);
798 if (!childDoc
|| childDoc
->ParentDoc() != this) return false;
800 if (!childDoc
->CheckDocTree()) {
808 xpcAccessibleGeneric
* DocAccessibleParent::GetXPCAccessible(
809 RemoteAccessible
* aProxy
) {
810 xpcAccessibleDocument
* doc
= GetAccService()->GetXPCDocument(this);
813 return doc
->GetAccessible(aProxy
);
817 void DocAccessibleParent::MaybeInitWindowEmulation() {
818 if (!nsWinUtils::IsWindowEmulationStarted()) {
822 // XXX get the bounds from the browserParent instead of poking at accessibles
823 // which might not exist yet.
824 LocalAccessible
* outerDoc
= OuterDocOfRemoteBrowser();
829 RootAccessible
* rootDocument
= outerDoc
->RootAccessible();
830 MOZ_ASSERT(rootDocument
);
832 bool isActive
= true;
833 nsIntRect
rect(CW_USEDEFAULT
, CW_USEDEFAULT
, 0, 0);
834 if (Compatibility::IsDolphin()) {
836 nsIntRect rootRect
= rootDocument
->Bounds();
837 rect
.MoveToX(rootRect
.X() - rect
.X());
838 rect
.MoveToY(rect
.Y() - rootRect
.Y());
840 auto browserParent
= static_cast<dom::BrowserParent
*>(Manager());
841 isActive
= browserParent
->GetDocShellIsActive();
844 // onCreate is guaranteed to be called synchronously by
845 // nsWinUtils::CreateNativeWindow, so this reference isn't really necessary.
846 // However, static analysis complains without it.
847 RefPtr
<DocAccessibleParent
> thisRef
= this;
848 nsWinUtils::NativeWindowCreateProc
onCreate([thisRef
](HWND aHwnd
) -> void {
849 IDispatchHolder hWndAccHolder
;
851 ::SetPropW(aHwnd
, kPropNameDocAccParent
,
852 reinterpret_cast<HANDLE
>(thisRef
.get()));
854 thisRef
->SetEmulatedWindowHandle(aHwnd
);
856 RefPtr
<IAccessible
> hwndAcc
;
857 if (SUCCEEDED(::AccessibleObjectFromWindow(
858 aHwnd
, OBJID_WINDOW
, IID_IAccessible
, getter_AddRefs(hwndAcc
)))) {
859 RefPtr
<IDispatch
> wrapped(
860 mscom::PassthruProxy::Wrap
<IDispatch
>(WrapNotNull(hwndAcc
)));
861 hWndAccHolder
.Set(IDispatchHolder::COMPtrType(
862 mscom::ToProxyUniquePtr(std::move(wrapped
))));
865 Unused
<< thisRef
->SendEmulatedWindow(
866 reinterpret_cast<uintptr_t>(thisRef
->mEmulatedWindowHandle
),
870 HWND parentWnd
= reinterpret_cast<HWND
>(rootDocument
->GetNativeWindow());
871 DebugOnly
<HWND
> hWnd
= nsWinUtils::CreateNativeWindow(
872 kClassNameTabContent
, parentWnd
, rect
.X(), rect
.Y(), rect
.Width(),
873 rect
.Height(), isActive
, &onCreate
);
877 void DocAccessibleParent::SendParentCOMProxy(LocalAccessible
* aOuterDoc
) {
878 // Make sure that we're not racing with a tab shutdown
879 auto tab
= static_cast<dom::BrowserParent
*>(Manager());
881 if (tab
->IsDestroyed()) {
885 RefPtr
<IAccessible
> nativeAcc
;
886 aOuterDoc
->GetNativeInterface(getter_AddRefs(nativeAcc
));
887 if (NS_WARN_IF(!nativeAcc
)) {
888 // Couldn't get a COM proxy for the outer doc. That probably means it died,
889 // but the parent process hasn't received a message to remove it from the
890 // RemoteAccessible tree yet.
894 RefPtr
<IDispatch
> wrapped(
895 mscom::PassthruProxy::Wrap
<IDispatch
>(WrapNotNull(nativeAcc
)));
897 IDispatchHolder::COMPtrType
ptr(mscom::ToProxyUniquePtr(std::move(wrapped
)));
898 IDispatchHolder
holder(std::move(ptr
));
899 if (!PDocAccessibleParent::SendParentCOMProxy(holder
)) {
903 # if defined(MOZ_SANDBOX)
904 mParentProxyStream
= holder
.GetPreservedStream();
905 # endif // defined(MOZ_SANDBOX)
908 void DocAccessibleParent::SetEmulatedWindowHandle(HWND aWindowHandle
) {
909 if (!aWindowHandle
&& mEmulatedWindowHandle
&& IsTopLevel()) {
910 ::DestroyWindow(mEmulatedWindowHandle
);
912 mEmulatedWindowHandle
= aWindowHandle
;
915 mozilla::ipc::IPCResult
DocAccessibleParent::RecvFocusEvent(
916 const uint64_t& aID
, const LayoutDeviceIntRect
& aCaretRect
) {
921 RemoteAccessible
* proxy
= GetAccessible(aID
);
923 NS_ERROR("no proxy for event!");
927 ProxyFocusEvent(proxy
, aCaretRect
);
929 if (!nsCoreUtils::AccEventObserversExist()) {
933 xpcAccessibleGeneric
* xpcAcc
= GetXPCAccessible(proxy
);
934 xpcAccessibleDocument
* doc
= GetAccService()->GetXPCDocument(this);
935 nsINode
* node
= nullptr;
936 bool fromUser
= true; // XXX fix me
937 RefPtr
<xpcAccEvent
> event
= new xpcAccEvent(nsIAccessibleEvent::EVENT_FOCUS
,
938 xpcAcc
, doc
, node
, fromUser
);
939 nsCoreUtils::DispatchAccEvent(std::move(event
));
944 #endif // defined(XP_WIN)
947 mozilla::ipc::IPCResult
DocAccessibleParent::RecvBatch(
948 const uint64_t& aBatchType
, nsTArray
<BatchData
>&& aData
) {
949 // Only do something in Android. We can't ifdef the entire protocol out in
950 // the ipdl because it doesn't allow preprocessing.
951 # if defined(ANDROID)
955 nsTArray
<RemoteAccessible
*> proxies(aData
.Length());
956 for (size_t i
= 0; i
< aData
.Length(); i
++) {
957 DocAccessibleParent
* doc
= static_cast<DocAccessibleParent
*>(
958 aData
.ElementAt(i
).Document().get_PDocAccessibleParent());
961 if (doc
->IsShutdown()) {
965 RemoteAccessible
* proxy
= doc
->GetAccessible(aData
.ElementAt(i
).ID());
967 MOZ_ASSERT_UNREACHABLE("No proxy found!");
971 proxies
.AppendElement(proxy
);
973 ProxyBatch(this, aBatchType
, proxies
, aData
);
974 # endif // defined(XP_WIN)
978 bool DocAccessibleParent::DeallocPDocAccessiblePlatformExtParent(
979 PDocAccessiblePlatformExtParent
* aActor
) {
984 PDocAccessiblePlatformExtParent
*
985 DocAccessibleParent::AllocPDocAccessiblePlatformExtParent() {
986 return new DocAccessiblePlatformExtParent();
989 DocAccessiblePlatformExtParent
* DocAccessibleParent::GetPlatformExtension() {
990 return static_cast<DocAccessiblePlatformExtParent
*>(
991 SingleManagedOrNull(ManagedPDocAccessiblePlatformExtParent()));
994 #endif // !defined(XP_WIN)
996 Tuple
<DocAccessibleParent
*, uint64_t> DocAccessibleParent::GetRemoteEmbedder() {
997 dom::BrowserParent
* embeddedBrowser
= dom::BrowserParent::GetFrom(Manager());
998 dom::BrowserBridgeParent
* bridge
= embeddedBrowser
->GetBrowserBridgeParent();
1000 return Tuple
<DocAccessibleParent
*, uint64_t>(nullptr, 0);
1002 DocAccessibleParent
* doc
;
1004 Tie(doc
, id
) = bridge
->GetEmbedderAccessible();
1005 if (doc
&& doc
->IsShutdown()) {
1006 // Sometimes, the embedder document is destroyed before its
1007 // BrowserBridgeParent. Don't return a destroyed document.
1011 return Tuple
<DocAccessibleParent
*, uint64_t>(doc
, id
);
1014 void DocAccessibleParent::RemovePendingChildDoc(DocAccessibleParent
* aChildDoc
,
1015 uint64_t aParentID
) {
1016 for (uint32_t index
= 0, len
= mPendingChildDocs
.Length(); index
< len
;
1018 PendingChildDoc
& pending
= mPendingChildDocs
[index
];
1019 if (pending
.mParentID
== aParentID
) {
1020 MOZ_ASSERT(pending
.mChildDoc
== aChildDoc
);
1021 mPendingChildDocs
.RemoveElementAt(index
);
1028 } // namespace mozilla