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"
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"
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.
44 NS_ERROR("adding child to unknown accessible");
46 return IPC_FAIL(this, "unknown parent accessible");
52 uint32_t newChildIdx
= aData
.Idx();
53 if (newChildIdx
> parent
->ChildrenCount()) {
54 NS_ERROR("invalid index to add child at");
56 return IPC_FAIL(this, "invalid index");
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.
68 return IPC_FAIL(this, "failed to add children");
72 for (uint32_t i
= 0; i
< consumed
; i
++) {
73 uint64_t id
= aData
.NewTree()[i
].ID();
74 MOZ_ASSERT(mAccessibles
.GetEntry(id
));
78 MOZ_ASSERT(CheckDocTree());
80 // Just update, no events.
81 if (aData
.EventSuppressed()) {
85 ProxyAccessible
* target
= parent
->ChildAt(newChildIdx
);
86 ProxyShowHideEvent(target
, parent
, true, aFromUser
);
88 if (!nsCoreUtils::AccEventObserversExist()) {
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
));
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!");
111 const AccessibleData
& newChild
= aNewTree
[aIdx
];
113 if (mAccessibles
.Contains(newChild
.ID())) {
114 NS_ERROR("ID already in use");
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());
126 WrapperFor(newProxy
)->SetID(newChild
.MsaaID());
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
);
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.
152 return IPC_FAIL(this, "Trying to hide entire document?");
155 ProxyEntry
* rootEntry
= mAccessibles
.GetEntry(aRootID
);
157 NS_ERROR("invalid root being removed!");
161 ProxyAccessible
* root
= rootEntry
->mProxy
;
163 NS_ERROR("invalid root being removed!");
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
,
185 parent
->RemoveChild(root
);
188 MOZ_ASSERT(CheckDocTree());
191 nsCoreUtils::DispatchAccEvent(std::move(event
));
197 mozilla::ipc::IPCResult
DocAccessibleParent::RecvEvent(
198 const uint64_t& aID
, const uint32_t& aEventType
) {
203 ProxyAccessible
* proxy
= GetAccessible(aID
);
205 NS_ERROR("no proxy for event!");
209 ProxyEvent(proxy
, aEventType
);
211 if (!nsCoreUtils::AccEventObserversExist()) {
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
));
226 mozilla::ipc::IPCResult
DocAccessibleParent::RecvStateChangeEvent(
227 const uint64_t& aID
, const uint64_t& aState
, const bool& aEnabled
) {
232 ProxyAccessible
* target
= GetAccessible(aID
);
234 NS_ERROR("we don't know about the target of a state change event!");
238 ProxyStateChangeEvent(target
, aState
, aEnabled
);
240 if (!nsCoreUtils::AccEventObserversExist()) {
244 xpcAccessibleGeneric
* xpcAcc
= GetXPCAccessible(target
);
245 xpcAccessibleDocument
* doc
= GetAccService()->GetXPCDocument(this);
246 uint32_t type
= nsIAccessibleEvent::EVENT_STATE_CHANGE
;
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
));
258 mozilla::ipc::IPCResult
DocAccessibleParent::RecvCaretMoveEvent(
261 const LayoutDeviceIntRect
& aCaretRect
,
262 #endif // defined (XP_WIN)
263 const int32_t& aOffset
) {
268 ProxyAccessible
* proxy
= GetAccessible(aID
);
270 NS_ERROR("unknown caret move event target!");
275 ProxyCaretMoveEvent(proxy
, aCaretRect
);
277 ProxyCaretMoveEvent(proxy
, aOffset
);
280 if (!nsCoreUtils::AccEventObserversExist()) {
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
));
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
) {
303 ProxyAccessible
* target
= GetAccessible(aID
);
305 NS_ERROR("text change event target is unknown!");
309 ProxyTextChangeEvent(target
, aStr
, aStart
, aLen
, aIsInsert
, aFromUser
);
311 if (!nsCoreUtils::AccEventObserversExist()) {
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
));
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
) {
343 ProxyAccessible
* target
= GetAccessible(aID
);
344 ProxyAccessible
* widget
= GetAccessible(aWidgetID
);
345 if (!target
|| !widget
) {
346 NS_ERROR("invalid id in selection event");
350 ProxySelectionEvent(target
, widget
, aType
);
351 if (!nsCoreUtils::AccEventObserversExist()) {
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
));
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
);
374 NS_ERROR("no proxy for event!");
379 ProxyVirtualCursorChangeEvent(
380 target
, oldPosition
, aOldStartOffset
, aOldEndOffset
, newPosition
,
381 aNewStartOffset
, aNewEndOffset
, aReason
, aBoundaryType
, aFromUser
);
384 if (!nsCoreUtils::AccEventObserversExist()) {
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
));
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
);
408 NS_ERROR("no proxy for event!");
413 ProxyScrollingEvent(target
, aType
, aScrollX
, aScrollY
, aMaxScrollX
,
416 ProxyEvent(target
, aType
);
419 if (!nsCoreUtils::AccEventObserversExist()) {
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
));
436 mozilla::ipc::IPCResult
DocAccessibleParent::RecvAnnouncementEvent(
437 const uint64_t& aID
, const nsString
& aAnnouncement
,
438 const uint16_t& aPriority
) {
439 ProxyAccessible
* target
= GetAccessible(aID
);
442 NS_ERROR("no proxy for event!");
446 # if defined(ANDROID)
447 ProxyAnnouncementEvent(target
, aAnnouncement
, aPriority
);
450 if (!nsCoreUtils::AccEventObserversExist()) {
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
));
465 mozilla::ipc::IPCResult
DocAccessibleParent::RecvRoleChangedEvent(
466 const a11y::role
& aRole
) {
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.
480 if (!aID
) return IPC_FAIL(this, "ID is 0!");
486 MOZ_ASSERT(CheckDocTree());
488 auto childDoc
= static_cast<DocAccessibleParent
*>(aChildDoc
);
490 ipc::IPCResult result
= AddChildDoc(childDoc
, aID
, false);
492 MOZ_ASSERT(CheckDocTree());
504 ipc::IPCResult
DocAccessibleParent::AddChildDoc(DocAccessibleParent
* aChildDoc
,
507 // We do not use GetAccessible here because we want to be sure to not get the
509 ProxyEntry
* e
= mAccessibles
.GetEntry(aParentID
);
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
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
;
536 ProxyCreated(aChildDoc
, Interfaces::DOCUMENT
| Interfaces::HYPERTEXT
);
540 auto embeddedBrowser
= static_cast<dom::BrowserParent
*>(aChildDoc
->Manager());
541 dom::BrowserBridgeParent
* bridge
= embeddedBrowser
->GetBrowserBridgeParent();
543 // aChildDoc is an embedded document in a different content process to
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)
579 mozilla::ipc::IPCResult
DocAccessibleParent::RecvShutdown() {
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
);
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.
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
);
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
);
628 DocAccessibleParent
* thisDoc
= LiveDocs().Get(actorID
);
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
);
646 ProxyDestroyed(thisDoc
);
647 thisDoc
= LiveDocs().Get(actorID
);
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
) {
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()) {
681 xpcAccessibleGeneric
* DocAccessibleParent::GetXPCAccessible(
682 ProxyAccessible
* aProxy
) {
683 xpcAccessibleDocument
* doc
= GetAccService()->GetXPCDocument(this);
686 return doc
->GetXPCAccessible(aProxy
);
690 void DocAccessibleParent::MaybeInitWindowEmulation() {
691 if (!nsWinUtils::IsWindowEmulationStarted()) {
695 // XXX get the bounds from the browserParent instead of poking at accessibles
696 // which might not exist yet.
697 Accessible
* outerDoc
= OuterDocOfRemoteBrowser();
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()) {
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
),
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
);
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());
754 if (tab
->IsDestroyed()) {
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
)) {
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
);
790 // We're seeing this in the wild - the plugin is windowed but we no longer
795 IAccessible
* rawAccPlugin
= nullptr;
796 HRESULT hr
= ::AccessibleObjectFromWindow(
797 childWnd
, OBJID_WINDOW
, IID_IAccessible
, (void**)&rawAccPlugin
);
799 // This might happen if the plugin doesn't handle WM_GETOBJECT properly.
800 // We should not consider that a failure.
804 aPluginCOMProxy
->Set(IAccessibleHolder::COMPtrType(rawAccPlugin
));
808 return IPC_FAIL(this, "Message unsupported in this build configuration");
812 mozilla::ipc::IPCResult
DocAccessibleParent::RecvFocusEvent(
813 const uint64_t& aID
, const LayoutDeviceIntRect
& aCaretRect
) {
818 ProxyAccessible
* proxy
= GetAccessible(aID
);
820 NS_ERROR("no proxy for event!");
824 ProxyFocusEvent(proxy
, aCaretRect
);
826 if (!nsCoreUtils::AccEventObserversExist()) {
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
));
841 #endif // 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());
855 ProxyAccessible
* proxy
= doc
->GetAccessible(aData
.ElementAt(i
).ID());
857 MOZ_ASSERT_UNREACHABLE("No proxy found!");
861 proxies
.AppendElement(proxy
);
863 ProxyBatch(this, aBatchType
, proxies
, aData
);
864 # endif // defined(XP_WIN)
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();
873 return Tuple
<DocAccessibleParent
*, uint64_t>(nullptr, 0);
875 DocAccessibleParent
* doc
;
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.
884 return Tuple
<DocAccessibleParent
*, uint64_t>(doc
, id
);
888 } // namespace mozilla