diff options
Diffstat (limited to 'accessible/ipc/DocAccessibleParent.h')
-rw-r--r-- | accessible/ipc/DocAccessibleParent.h | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/accessible/ipc/DocAccessibleParent.h b/accessible/ipc/DocAccessibleParent.h new file mode 100644 index 0000000000..9d7919c008 --- /dev/null +++ b/accessible/ipc/DocAccessibleParent.h @@ -0,0 +1,348 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_a11y_DocAccessibleParent_h +#define mozilla_a11y_DocAccessibleParent_h + +#include "nsAccessibilityService.h" +#include "mozilla/a11y/PDocAccessibleParent.h" +#include "mozilla/a11y/ProxyAccessible.h" +#include "mozilla/Tuple.h" +#include "nsClassHashtable.h" +#include "nsHashKeys.h" +#include "nsISupportsImpl.h" + +namespace mozilla { +namespace a11y { + +class xpcAccessibleGeneric; + +#if !defined(XP_WIN) +class DocAccessiblePlatformExtParent; +#endif + +/* + * These objects live in the main process and comunicate with and represent + * an accessible document in a content process. + */ +class DocAccessibleParent : public ProxyAccessible, + public PDocAccessibleParent { + public: + NS_INLINE_DECL_REFCOUNTING(DocAccessibleParent); + + DocAccessibleParent() + : ProxyAccessible(this), + mParentDoc(kNoParentDoc), +#if defined(XP_WIN) + mEmulatedWindowHandle(nullptr), +#endif // defined(XP_WIN) + mTopLevel(false), + mTopLevelInContentProcess(false), + mShutdown(false) { + sMaxDocID++; + mActorID = sMaxDocID; + MOZ_ASSERT(!LiveDocs().Get(mActorID)); + LiveDocs().Put(mActorID, this); + } + + /** + * Set this as a top level document; i.e. it is not embedded by another remote + * document. This also means it is a top level document in its content + * process. + * Tab documents are top level documents. + */ + void SetTopLevel() { + mTopLevel = true; + mTopLevelInContentProcess = true; + } + bool IsTopLevel() const { return mTopLevel; } + + /** + * Set this as a top level document in its content process. + * Note that this could be an out-of-process iframe embedded by a remote + * embedder document. In that case, IsToplevel() will return false, but + * IsTopLevelInContentProcess() will return true. + */ + void SetTopLevelInContentProcess() { mTopLevelInContentProcess = true; } + bool IsTopLevelInContentProcess() const { return mTopLevelInContentProcess; } + + bool IsShutdown() const { return mShutdown; } + + /** + * Mark this actor as shutdown without doing any cleanup. This should only + * be called on actors that have just been initialized, so probably only from + * RecvPDocAccessibleConstructor. + */ + void MarkAsShutdown() { + MOZ_ASSERT(mChildDocs.IsEmpty()); + MOZ_ASSERT(mAccessibles.Count() == 0); + mShutdown = true; + } + + /* + * Called when a message from a document in a child process notifies the main + * process it is firing an event. + */ + virtual mozilla::ipc::IPCResult RecvEvent(const uint64_t& aID, + const uint32_t& aType) override; + + virtual mozilla::ipc::IPCResult RecvShowEvent(const ShowEventData& aData, + const bool& aFromUser) override; + virtual mozilla::ipc::IPCResult RecvHideEvent(const uint64_t& aRootID, + const bool& aFromUser) override; + mozilla::ipc::IPCResult RecvStateChangeEvent(const uint64_t& aID, + const uint64_t& aState, + const bool& aEnabled) final; + + mozilla::ipc::IPCResult RecvCaretMoveEvent( + const uint64_t& aID, +#if defined(XP_WIN) + const LayoutDeviceIntRect& aCaretRect, +#endif + const int32_t& aOffset, const bool& aIsSelectionCollapsed) final; + + virtual mozilla::ipc::IPCResult RecvTextChangeEvent( + const uint64_t& aID, const nsString& aStr, const int32_t& aStart, + const uint32_t& aLen, const bool& aIsInsert, + const bool& aFromUser) override; + +#if defined(XP_WIN) + virtual mozilla::ipc::IPCResult RecvSyncTextChangeEvent( + const uint64_t& aID, const nsString& aStr, const int32_t& aStart, + const uint32_t& aLen, const bool& aIsInsert, + const bool& aFromUser) override; + + virtual mozilla::ipc::IPCResult RecvFocusEvent( + const uint64_t& aID, const LayoutDeviceIntRect& aCaretRect) override; +#endif // defined(XP_WIN) + + virtual mozilla::ipc::IPCResult RecvSelectionEvent( + const uint64_t& aID, const uint64_t& aWidgetID, + const uint32_t& aType) override; + + MOZ_CAN_RUN_SCRIPT_BOUNDARY + virtual mozilla::ipc::IPCResult RecvVirtualCursorChangeEvent( + const uint64_t& aID, const uint64_t& aOldPositionID, + const int32_t& aOldStartOffset, const int32_t& aOldEndOffset, + const uint64_t& aNewPositionID, const int32_t& aNewStartOffset, + const int32_t& aNewEndOffset, const int16_t& aReason, + const int16_t& aBoundaryType, const bool& aFromUser) override; + + virtual mozilla::ipc::IPCResult RecvScrollingEvent( + const uint64_t& aID, const uint64_t& aType, const uint32_t& aScrollX, + const uint32_t& aScrollY, const uint32_t& aMaxScrollX, + const uint32_t& aMaxScrollY) override; + +#if !defined(XP_WIN) + virtual mozilla::ipc::IPCResult RecvAnnouncementEvent( + const uint64_t& aID, const nsString& aAnnouncement, + const uint16_t& aPriority) override; + + virtual mozilla::ipc::IPCResult RecvTextSelectionChangeEvent( + const uint64_t& aID, nsTArray<TextRangeData>&& aSelection) override; +#endif + + mozilla::ipc::IPCResult RecvRoleChangedEvent(const a11y::role& aRole) final; + + virtual mozilla::ipc::IPCResult RecvBindChildDoc( + PDocAccessibleParent* aChildDoc, const uint64_t& aID) override; + + void Unbind() { + if (DocAccessibleParent* parent = ParentDoc()) { + parent->RemoveChildDoc(this); + } + + SetParent(nullptr); + } + + virtual mozilla::ipc::IPCResult RecvShutdown() override; + void Destroy(); + virtual void ActorDestroy(ActorDestroyReason aWhy) override { + MOZ_ASSERT(CheckDocTree()); + if (!mShutdown) Destroy(); + } + + /* + * Return the main processes representation of the parent document (if any) + * of the document this object represents. + */ + DocAccessibleParent* ParentDoc() const; + static const uint64_t kNoParentDoc = UINT64_MAX; + + /* + * Called when a document in a content process notifies the main process of a + * new child document. + */ + ipc::IPCResult AddChildDoc(DocAccessibleParent* aChildDoc, uint64_t aParentID, + bool aCreating = true); + + /* + * Called when the document in the content process this object represents + * notifies the main process a child document has been removed. + */ + void RemoveChildDoc(DocAccessibleParent* aChildDoc) { + ProxyAccessible* parent = aChildDoc->Parent(); + MOZ_ASSERT(parent); + if (parent) { + aChildDoc->Parent()->ClearChildDoc(aChildDoc); + } + DebugOnly<bool> result = mChildDocs.RemoveElement(aChildDoc->mActorID); + aChildDoc->mParentDoc = kNoParentDoc; + MOZ_ASSERT(result); + } + + void RemoveAccessible(ProxyAccessible* aAccessible) { + MOZ_DIAGNOSTIC_ASSERT(mAccessibles.GetEntry(aAccessible->ID())); + mAccessibles.RemoveEntry(aAccessible->ID()); + } + + /** + * Return the accessible for given id. + */ + ProxyAccessible* GetAccessible(uintptr_t aID) { + if (!aID) return this; + + ProxyEntry* e = mAccessibles.GetEntry(aID); + return e ? e->mProxy : nullptr; + } + + const ProxyAccessible* GetAccessible(uintptr_t aID) const { + return const_cast<DocAccessibleParent*>(this)->GetAccessible(aID); + } + + size_t ChildDocCount() const { return mChildDocs.Length(); } + const DocAccessibleParent* ChildDocAt(size_t aIdx) const { + return const_cast<DocAccessibleParent*>(this)->ChildDocAt(aIdx); + } + DocAccessibleParent* ChildDocAt(size_t aIdx) { + return LiveDocs().Get(mChildDocs[aIdx]); + } + +#if defined(XP_WIN) + void MaybeInitWindowEmulation(); + + /** + * Note that an OuterDocAccessible can be created before the + * DocAccessibleParent or vice versa. Therefore, this must be conditionally + * called when either of these is created. + * @param aOuterDoc The OuterDocAccessible to be returned as the parent of + * this document. Only GetNativeInterface() is called on this, so it + * may be a ProxyAccessibleWrap or similar. + */ + void SendParentCOMProxy(Accessible* aOuterDoc); + + virtual mozilla::ipc::IPCResult RecvGetWindowedPluginIAccessible( + const WindowsHandle& aHwnd, IAccessibleHolder* aPluginCOMProxy) override; + + /** + * Set emulated native window handle for a document. + * @param aWindowHandle emulated native window handle + */ + void SetEmulatedWindowHandle(HWND aWindowHandle); + HWND GetEmulatedWindowHandle() const { return mEmulatedWindowHandle; } +#endif + +#if !defined(XP_WIN) + virtual mozilla::ipc::IPCResult RecvBatch( + const uint64_t& aBatchType, nsTArray<BatchData>&& aData) override; + + virtual bool DeallocPDocAccessiblePlatformExtParent( + PDocAccessiblePlatformExtParent* aActor) override; + + virtual PDocAccessiblePlatformExtParent* + AllocPDocAccessiblePlatformExtParent() override; + + DocAccessiblePlatformExtParent* GetPlatformExtension(); +#endif + + /** + * If this is an iframe document rendered in a different process to its + * embedder, return the DocAccessibleParent and id for the embedder + * accessible. Otherwise, return null and 0. + */ + Tuple<DocAccessibleParent*, uint64_t> GetRemoteEmbedder(); + + private: + ~DocAccessibleParent() { + LiveDocs().Remove(mActorID); + MOZ_ASSERT(mChildDocs.Length() == 0); + MOZ_ASSERT(!ParentDoc()); + } + + class ProxyEntry : public PLDHashEntryHdr { + public: + explicit ProxyEntry(const void*) : mProxy(nullptr) {} + ProxyEntry(ProxyEntry&& aOther) : mProxy(aOther.mProxy) { + aOther.mProxy = nullptr; + } + ~ProxyEntry() { delete mProxy; } + + typedef uint64_t KeyType; + typedef const void* KeyTypePointer; + + bool KeyEquals(const void* aKey) const { + return mProxy->ID() == (uint64_t)aKey; + } + + static const void* KeyToPointer(uint64_t aKey) { return (void*)aKey; } + + static PLDHashNumber HashKey(const void* aKey) { return (uint64_t)aKey; } + + enum { ALLOW_MEMMOVE = true }; + + ProxyAccessible* mProxy; + }; + + uint32_t AddSubtree(ProxyAccessible* aParent, + const nsTArray<AccessibleData>& aNewTree, uint32_t aIdx, + uint32_t aIdxInParent); + [[nodiscard]] bool CheckDocTree() const; + xpcAccessibleGeneric* GetXPCAccessible(ProxyAccessible* aProxy); + + nsTArray<uint64_t> mChildDocs; + uint64_t mParentDoc; + +#if defined(XP_WIN) + // The handle associated with the emulated window that contains this document + HWND mEmulatedWindowHandle; + +# if defined(MOZ_SANDBOX) + mscom::PreservedStreamPtr mParentProxyStream; + mscom::PreservedStreamPtr mDocProxyStream; + mscom::PreservedStreamPtr mTopLevelDocProxyStream; +# endif // defined(MOZ_SANDBOX) +#endif // defined(XP_WIN) + + /* + * Conceptually this is a map from IDs to proxies, but we store the ID in the + * proxy object so we can't use a real map. + */ + nsTHashtable<ProxyEntry> mAccessibles; + uint64_t mActorID; + bool mTopLevel; + bool mTopLevelInContentProcess; + bool mShutdown; + + struct PendingChildDoc { + PendingChildDoc(DocAccessibleParent* aChildDoc, uint64_t aParentID) + : mChildDoc(aChildDoc), mParentID(aParentID) {} + RefPtr<DocAccessibleParent> mChildDoc; + uint64_t mParentID; + }; + // We use nsTArray because there will be very few entries. + nsTArray<PendingChildDoc> mPendingChildDocs; + + static uint64_t sMaxDocID; + static nsDataHashtable<nsUint64HashKey, DocAccessibleParent*>& LiveDocs() { + static nsDataHashtable<nsUint64HashKey, DocAccessibleParent*> sLiveDocs; + return sLiveDocs; + } +}; + +} // namespace a11y +} // namespace mozilla + +#endif |