/* -*- 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&& 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 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(this)->GetAccessible(aID); } size_t ChildDocCount() const { return mChildDocs.Length(); } const DocAccessibleParent* ChildDocAt(size_t aIdx) const { return const_cast(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&& 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 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& aNewTree, uint32_t aIdx, uint32_t aIdxInParent); [[nodiscard]] bool CheckDocTree() const; xpcAccessibleGeneric* GetXPCAccessible(ProxyAccessible* aProxy); nsTArray 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 mAccessibles; uint64_t mActorID; bool mTopLevel; bool mTopLevelInContentProcess; bool mShutdown; struct PendingChildDoc { PendingChildDoc(DocAccessibleParent* aChildDoc, uint64_t aParentID) : mChildDoc(aChildDoc), mParentID(aParentID) {} RefPtr mChildDoc; uint64_t mParentID; }; // We use nsTArray because there will be very few entries. nsTArray mPendingChildDocs; static uint64_t sMaxDocID; static nsDataHashtable& LiveDocs() { static nsDataHashtable sLiveDocs; return sLiveDocs; } }; } // namespace a11y } // namespace mozilla #endif