summaryrefslogtreecommitdiffstats
path: root/accessible/ipc/DocAccessibleParent.h
diff options
context:
space:
mode:
Diffstat (limited to 'accessible/ipc/DocAccessibleParent.h')
-rw-r--r--accessible/ipc/DocAccessibleParent.h400
1 files changed, 400 insertions, 0 deletions
diff --git a/accessible/ipc/DocAccessibleParent.h b/accessible/ipc/DocAccessibleParent.h
new file mode 100644
index 0000000000..bb05fbafad
--- /dev/null
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -0,0 +1,400 @@
+/* -*- 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/RemoteAccessible.h"
+#include "mozilla/dom/BrowserBridgeParent.h"
+#include "nsClassHashtable.h"
+#include "nsHashKeys.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace dom {
+class CanonicalBrowsingContext;
+}
+
+namespace a11y {
+
+class TextRange;
+class xpcAccessibleGeneric;
+
+/*
+ * These objects live in the main process and comunicate with and represent
+ * an accessible document in a content process.
+ */
+class DocAccessibleParent : public RemoteAccessible,
+ public PDocAccessibleParent,
+ public nsIMemoryReporter {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMEMORYREPORTER
+
+ private:
+ DocAccessibleParent();
+
+ public:
+ static already_AddRefed<DocAccessibleParent> New();
+
+ /**
+ * 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; }
+
+ /**
+ * Determine whether this is an out-of-process iframe document, embedded by a
+ * remote embedder document.
+ */
+ bool IsOOPIframeDoc() const {
+ return !mTopLevel && 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);
+ MOZ_ASSERT(!mBrowsingContext);
+ mShutdown = true;
+ }
+
+ void SetBrowsingContext(dom::CanonicalBrowsingContext* aBrowsingContext);
+
+ dom::CanonicalBrowsingContext* GetBrowsingContext() const {
+ return mBrowsingContext;
+ }
+
+ /*
+ * 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(
+ nsTArray<AccessibleData>&& aNewTree, const bool& aEventSuppressed,
+ const bool& aComplete, 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, const LayoutDeviceIntRect& aCaretRect,
+ const int32_t& aOffset, const bool& aIsSelectionCollapsed,
+ const bool& aIsAtEndOfLine, const int32_t& aGranularity,
+ const bool& aFromUser) final;
+
+ virtual mozilla::ipc::IPCResult RecvTextChangeEvent(
+ const uint64_t& aID, const nsAString& 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;
+
+ virtual mozilla::ipc::IPCResult RecvSelectionEvent(
+ const uint64_t& aID, const uint64_t& aWidgetID,
+ const uint32_t& aType) 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;
+
+ virtual mozilla::ipc::IPCResult RecvCache(
+ const mozilla::a11y::CacheUpdateType& aUpdateType,
+ nsTArray<CacheData>&& aData) override;
+
+ virtual mozilla::ipc::IPCResult RecvSelectedAccessiblesChanged(
+ nsTArray<uint64_t>&& aSelectedIDs,
+ nsTArray<uint64_t>&& aUnselectedIDs) override;
+
+ virtual mozilla::ipc::IPCResult RecvAccessiblesWillMove(
+ nsTArray<uint64_t>&& aIDs) override;
+
+#if !defined(XP_WIN)
+ virtual mozilla::ipc::IPCResult RecvAnnouncementEvent(
+ const uint64_t& aID, const nsAString& aAnnouncement,
+ const uint16_t& aPriority) override;
+#endif
+
+ virtual mozilla::ipc::IPCResult RecvTextSelectionChangeEvent(
+ const uint64_t& aID, nsTArray<TextRangeData>&& aSelection) override;
+
+ mozilla::ipc::IPCResult RecvRoleChangedEvent(
+ const a11y::role& aRole, const uint8_t& aRoleMapEntryIndex) final;
+
+ virtual mozilla::ipc::IPCResult RecvBindChildDoc(
+ NotNull<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;
+
+ /*
+ * 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.
+ * Although this is called internally for OOP child documents, these should be
+ * added via the BrowserBridgeParent version of this method, as the parent id
+ * might not exist yet in that case.
+ */
+ ipc::IPCResult AddChildDoc(DocAccessibleParent* aChildDoc, uint64_t aParentID,
+ bool aCreating = true);
+
+ /**
+ * Called when a document in a content process notifies the main process of a
+ * new OOP child document.
+ */
+ ipc::IPCResult AddChildDoc(dom::BrowserBridgeParent* aBridge);
+
+ void RemovePendingOOPChildDoc(dom::BrowserBridgeParent* aBridge) {
+ mPendingOOPChildDocs.Remove(aBridge);
+ }
+
+ /*
+ * 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) {
+ RemoteAccessible* parent = aChildDoc->RemoteParent();
+ MOZ_ASSERT(parent);
+ if (parent) {
+ aChildDoc->RemoteParent()->ClearChildDoc(aChildDoc);
+ }
+ DebugOnly<bool> result = mChildDocs.RemoveElement(aChildDoc->mActorID);
+ aChildDoc->mParentDoc = kNoParentDoc;
+ MOZ_ASSERT(result);
+ }
+
+ void RemoveAccessible(RemoteAccessible* aAccessible) {
+ MOZ_DIAGNOSTIC_ASSERT(mAccessibles.GetEntry(aAccessible->ID()));
+ mAccessibles.RemoveEntry(aAccessible->ID());
+ }
+
+ /**
+ * Return the accessible for given id.
+ */
+ RemoteAccessible* GetAccessible(uintptr_t aID) {
+ if (!aID) return this;
+
+ ProxyEntry* e = mAccessibles.GetEntry(aID);
+ return e ? e->mProxy : nullptr;
+ }
+
+ const RemoteAccessible* 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();
+
+ /**
+ * Set emulated native window handle for a document.
+ * @param aWindowHandle emulated native window handle
+ */
+ void SetEmulatedWindowHandle(HWND aWindowHandle);
+ HWND GetEmulatedWindowHandle() const { return mEmulatedWindowHandle; }
+#endif
+
+ // Accessible
+ virtual Accessible* Parent() const override {
+ if (IsTopLevel()) {
+ return OuterDocOfRemoteBrowser();
+ }
+ return RemoteParent();
+ }
+
+ virtual int32_t IndexInParent() const override {
+ if (IsTopLevel() && OuterDocOfRemoteBrowser()) {
+ // An OuterDoc can only have 1 child.
+ return 0;
+ }
+ return RemoteAccessible::IndexInParent();
+ }
+
+ /**
+ * Get the focused Accessible in this document, if any.
+ */
+ RemoteAccessible* GetFocusedAcc() const {
+ return const_cast<DocAccessibleParent*>(this)->GetAccessible(mFocus);
+ }
+
+ /**
+ * Get the HyperText Accessible containing the caret and the offset of the
+ * caret within. If there is no caret in this document, returns
+ * {nullptr, -1}.
+ */
+ std::pair<RemoteAccessible*, int32_t> GetCaret() const {
+ if (mCaretOffset == -1) {
+ return {nullptr, -1};
+ }
+ RemoteAccessible* acc =
+ const_cast<DocAccessibleParent*>(this)->GetAccessible(mCaretId);
+ if (!acc) {
+ return {nullptr, -1};
+ }
+ return {acc, mCaretOffset};
+ }
+
+ bool IsCaretAtEndOfLine() const { return mIsCaretAtEndOfLine; }
+
+ virtual void SelectionRanges(nsTArray<TextRange>* aRanges) const override;
+
+ virtual Accessible* FocusedChild() override;
+
+ void URL(nsAString& aURL) const;
+ void URL(nsACString& aURL) const;
+
+ void MimeType(nsAString& aURL) const;
+
+ virtual Relation RelationByType(RelationType aType) const override;
+
+ // Tracks cached reverse relations (ie. those not set explicitly by an
+ // attribute like aria-labelledby) for accessibles in this doc. This map is of
+ // the form: {accID, {relationType, [targetAccID, targetAccID, ...]}}
+ nsTHashMap<uint64_t, nsTHashMap<RelationType, nsTArray<uint64_t>>>
+ mReverseRelations;
+
+ // Computed from the viewport cache, the accs referenced by these ids
+ // are currently on screen (making any acc not in this list offscreen).
+ nsTHashSet<uint64_t> mOnScreenAccessibles;
+
+ static DocAccessibleParent* GetFrom(dom::BrowsingContext* aBrowsingContext);
+
+ size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) override;
+
+ private:
+ ~DocAccessibleParent();
+
+ 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 };
+
+ RemoteAccessible* mProxy;
+ };
+
+ RemoteAccessible* CreateAcc(const AccessibleData& aAccData);
+ void AttachChild(RemoteAccessible* aParent, uint32_t aIndex,
+ RemoteAccessible* aChild);
+ [[nodiscard]] bool CheckDocTree() const;
+ xpcAccessibleGeneric* GetXPCAccessible(RemoteAccessible* aProxy);
+
+ void FireEvent(RemoteAccessible* aAcc, const uint32_t& aType);
+
+ /**
+ * If this Accessible is being moved, prepare it for reuse. Otherwise, it is
+ * being removed, so shut it down.
+ */
+ void ShutdownOrPrepareForMove(RemoteAccessible* aAcc);
+
+ nsTArray<uint64_t> mChildDocs;
+ uint64_t mParentDoc;
+
+#if defined(XP_WIN)
+ // The handle associated with the emulated window that contains this document
+ HWND mEmulatedWindowHandle;
+#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 mPendingShowChild = 0;
+ uint64_t mPendingShowParent = 0;
+ uint32_t mPendingShowIndex = 0;
+ nsTHashSet<uint64_t> mMovingIDs;
+ uint64_t mActorID;
+ bool mTopLevel;
+ bool mTopLevelInContentProcess;
+ bool mShutdown;
+ RefPtr<dom::CanonicalBrowsingContext> mBrowsingContext;
+
+ nsTHashSet<RefPtr<dom::BrowserBridgeParent>> mPendingOOPChildDocs;
+
+ uint64_t mFocus;
+ uint64_t mCaretId;
+ int32_t mCaretOffset;
+ bool mIsCaretAtEndOfLine;
+ nsTArray<TextRangeData> mTextSelections;
+
+ static uint64_t sMaxDocID;
+ static nsTHashMap<nsUint64HashKey, DocAccessibleParent*>& LiveDocs() {
+ static nsTHashMap<nsUint64HashKey, DocAccessibleParent*> sLiveDocs;
+ return sLiveDocs;
+ }
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif