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 #ifndef mozilla_a11y_DocAccessibleParent_h
8 #define mozilla_a11y_DocAccessibleParent_h
10 #include "nsAccessibilityService.h"
11 #include "mozilla/a11y/PDocAccessibleParent.h"
12 #include "mozilla/a11y/RemoteAccessible.h"
13 #include "mozilla/dom/BrowserBridgeParent.h"
14 #include "nsClassHashtable.h"
15 #include "nsHashKeys.h"
16 #include "nsISupportsImpl.h"
20 class CanonicalBrowsingContext
;
26 class xpcAccessibleGeneric
;
29 * These objects live in the main process and comunicate with and represent
30 * an accessible document in a content process.
32 class DocAccessibleParent
: public RemoteAccessible
,
33 public PDocAccessibleParent
,
34 public nsIMemoryReporter
{
37 NS_DECL_NSIMEMORYREPORTER
40 DocAccessibleParent();
43 static already_AddRefed
<DocAccessibleParent
> New();
46 * Set this as a top level document; i.e. it is not embedded by another remote
47 * document. This also means it is a top level document in its content
49 * Tab documents are top level documents.
53 mTopLevelInContentProcess
= true;
55 bool IsTopLevel() const { return mTopLevel
; }
58 * Set this as a top level document in its content process.
59 * Note that this could be an out-of-process iframe embedded by a remote
60 * embedder document. In that case, IsToplevel() will return false, but
61 * IsTopLevelInContentProcess() will return true.
63 void SetTopLevelInContentProcess() { mTopLevelInContentProcess
= true; }
64 bool IsTopLevelInContentProcess() const { return mTopLevelInContentProcess
; }
67 * Determine whether this is an out-of-process iframe document, embedded by a
68 * remote embedder document.
70 bool IsOOPIframeDoc() const {
71 return !mTopLevel
&& mTopLevelInContentProcess
;
74 bool IsShutdown() const { return mShutdown
; }
77 * Mark this actor as shutdown without doing any cleanup. This should only
78 * be called on actors that have just been initialized, so probably only from
79 * RecvPDocAccessibleConstructor.
81 void MarkAsShutdown() {
82 MOZ_ASSERT(mChildDocs
.IsEmpty());
83 MOZ_ASSERT(mAccessibles
.Count() == 0);
84 MOZ_ASSERT(!mBrowsingContext
);
88 void SetBrowsingContext(dom::CanonicalBrowsingContext
* aBrowsingContext
);
90 dom::CanonicalBrowsingContext
* GetBrowsingContext() const {
91 return mBrowsingContext
;
95 * Called when a message from a document in a child process notifies the main
96 * process it is firing an event.
98 virtual mozilla::ipc::IPCResult
RecvEvent(const uint64_t& aID
,
99 const uint32_t& aType
) override
;
101 virtual mozilla::ipc::IPCResult
RecvShowEvent(
102 nsTArray
<AccessibleData
>&& aNewTree
, const bool& aEventSuppressed
,
103 const bool& aComplete
, const bool& aFromUser
) override
;
104 virtual mozilla::ipc::IPCResult
RecvHideEvent(const uint64_t& aRootID
,
105 const bool& aFromUser
) override
;
106 mozilla::ipc::IPCResult
RecvStateChangeEvent(const uint64_t& aID
,
107 const uint64_t& aState
,
108 const bool& aEnabled
) final
;
110 mozilla::ipc::IPCResult
RecvCaretMoveEvent(
111 const uint64_t& aID
, const LayoutDeviceIntRect
& aCaretRect
,
112 const int32_t& aOffset
, const bool& aIsSelectionCollapsed
,
113 const bool& aIsAtEndOfLine
, const int32_t& aGranularity
,
114 const bool& aFromUser
) final
;
116 virtual mozilla::ipc::IPCResult
RecvTextChangeEvent(
117 const uint64_t& aID
, const nsAString
& aStr
, const int32_t& aStart
,
118 const uint32_t& aLen
, const bool& aIsInsert
,
119 const bool& aFromUser
) override
;
121 virtual mozilla::ipc::IPCResult
RecvFocusEvent(
122 const uint64_t& aID
, const LayoutDeviceIntRect
& aCaretRect
) override
;
124 virtual mozilla::ipc::IPCResult
RecvSelectionEvent(
125 const uint64_t& aID
, const uint64_t& aWidgetID
,
126 const uint32_t& aType
) override
;
128 virtual mozilla::ipc::IPCResult
RecvScrollingEvent(
129 const uint64_t& aID
, const uint64_t& aType
, const uint32_t& aScrollX
,
130 const uint32_t& aScrollY
, const uint32_t& aMaxScrollX
,
131 const uint32_t& aMaxScrollY
) override
;
133 virtual mozilla::ipc::IPCResult
RecvCache(
134 const mozilla::a11y::CacheUpdateType
& aUpdateType
,
135 nsTArray
<CacheData
>&& aData
) override
;
137 virtual mozilla::ipc::IPCResult
RecvSelectedAccessiblesChanged(
138 nsTArray
<uint64_t>&& aSelectedIDs
,
139 nsTArray
<uint64_t>&& aUnselectedIDs
) override
;
141 virtual mozilla::ipc::IPCResult
RecvAccessiblesWillMove(
142 nsTArray
<uint64_t>&& aIDs
) override
;
145 virtual mozilla::ipc::IPCResult
RecvAnnouncementEvent(
146 const uint64_t& aID
, const nsAString
& aAnnouncement
,
147 const uint16_t& aPriority
) override
;
150 virtual mozilla::ipc::IPCResult
RecvTextSelectionChangeEvent(
151 const uint64_t& aID
, nsTArray
<TextRangeData
>&& aSelection
) override
;
153 mozilla::ipc::IPCResult
RecvRoleChangedEvent(
154 const a11y::role
& aRole
, const uint8_t& aRoleMapEntryIndex
) final
;
156 virtual mozilla::ipc::IPCResult
RecvBindChildDoc(
157 NotNull
<PDocAccessibleParent
*> aChildDoc
, const uint64_t& aID
) override
;
160 if (DocAccessibleParent
* parent
= ParentDoc()) {
161 parent
->RemoveChildDoc(this);
167 virtual mozilla::ipc::IPCResult
RecvShutdown() override
;
169 virtual void ActorDestroy(ActorDestroyReason aWhy
) override
;
172 * Return the main processes representation of the parent document (if any)
173 * of the document this object represents.
175 DocAccessibleParent
* ParentDoc() const;
176 static const uint64_t kNoParentDoc
= UINT64_MAX
;
179 * Called when a document in a content process notifies the main process of a
180 * new child document.
181 * Although this is called internally for OOP child documents, these should be
182 * added via the BrowserBridgeParent version of this method, as the parent id
183 * might not exist yet in that case.
185 ipc::IPCResult
AddChildDoc(DocAccessibleParent
* aChildDoc
, uint64_t aParentID
,
186 bool aCreating
= true);
189 * Called when a document in a content process notifies the main process of a
190 * new OOP child document.
192 ipc::IPCResult
AddChildDoc(dom::BrowserBridgeParent
* aBridge
);
194 void RemovePendingOOPChildDoc(dom::BrowserBridgeParent
* aBridge
) {
195 mPendingOOPChildDocs
.Remove(aBridge
);
199 * Called when the document in the content process this object represents
200 * notifies the main process a child document has been removed.
202 void RemoveChildDoc(DocAccessibleParent
* aChildDoc
) {
203 RemoteAccessible
* parent
= aChildDoc
->RemoteParent();
206 aChildDoc
->RemoteParent()->ClearChildDoc(aChildDoc
);
208 DebugOnly
<bool> result
= mChildDocs
.RemoveElement(aChildDoc
->mActorID
);
209 aChildDoc
->mParentDoc
= kNoParentDoc
;
213 void RemoveAccessible(RemoteAccessible
* aAccessible
) {
214 MOZ_DIAGNOSTIC_ASSERT(mAccessibles
.GetEntry(aAccessible
->ID()));
215 mAccessibles
.RemoveEntry(aAccessible
->ID());
219 * Return the accessible for given id.
221 RemoteAccessible
* GetAccessible(uintptr_t aID
) {
222 if (!aID
) return this;
224 ProxyEntry
* e
= mAccessibles
.GetEntry(aID
);
225 return e
? e
->mProxy
: nullptr;
228 const RemoteAccessible
* GetAccessible(uintptr_t aID
) const {
229 return const_cast<DocAccessibleParent
*>(this)->GetAccessible(aID
);
232 size_t ChildDocCount() const { return mChildDocs
.Length(); }
233 const DocAccessibleParent
* ChildDocAt(size_t aIdx
) const {
234 return const_cast<DocAccessibleParent
*>(this)->ChildDocAt(aIdx
);
236 DocAccessibleParent
* ChildDocAt(size_t aIdx
) {
237 return LiveDocs().Get(mChildDocs
[aIdx
]);
241 void MaybeInitWindowEmulation();
244 * Set emulated native window handle for a document.
245 * @param aWindowHandle emulated native window handle
247 void SetEmulatedWindowHandle(HWND aWindowHandle
);
248 HWND
GetEmulatedWindowHandle() const { return mEmulatedWindowHandle
; }
252 virtual Accessible
* Parent() const override
{
254 return OuterDocOfRemoteBrowser();
256 return RemoteParent();
259 virtual int32_t IndexInParent() const override
{
260 if (IsTopLevel() && OuterDocOfRemoteBrowser()) {
261 // An OuterDoc can only have 1 child.
264 return RemoteAccessible::IndexInParent();
268 * Get the focused Accessible in this document, if any.
270 RemoteAccessible
* GetFocusedAcc() const {
271 return const_cast<DocAccessibleParent
*>(this)->GetAccessible(mFocus
);
275 * Get the HyperText Accessible containing the caret and the offset of the
276 * caret within. If there is no caret in this document, returns
279 std::pair
<RemoteAccessible
*, int32_t> GetCaret() const {
280 if (mCaretOffset
== -1) {
281 return {nullptr, -1};
283 RemoteAccessible
* acc
=
284 const_cast<DocAccessibleParent
*>(this)->GetAccessible(mCaretId
);
286 return {nullptr, -1};
288 return {acc
, mCaretOffset
};
291 bool IsCaretAtEndOfLine() const { return mIsCaretAtEndOfLine
; }
293 virtual void SelectionRanges(nsTArray
<TextRange
>* aRanges
) const override
;
295 virtual Accessible
* FocusedChild() override
;
297 void URL(nsAString
& aURL
) const;
298 void URL(nsACString
& aURL
) const;
300 void MimeType(nsAString
& aURL
) const;
302 virtual Relation
RelationByType(RelationType aType
) const override
;
304 // Tracks cached reverse relations (ie. those not set explicitly by an
305 // attribute like aria-labelledby) for accessibles in this doc. This map is of
306 // the form: {accID, {relationType, [targetAccID, targetAccID, ...]}}
307 nsTHashMap
<uint64_t, nsTHashMap
<RelationType
, nsTArray
<uint64_t>>>
310 // Computed from the viewport cache, the accs referenced by these ids
311 // are currently on screen (making any acc not in this list offscreen).
312 nsTHashSet
<uint64_t> mOnScreenAccessibles
;
314 static DocAccessibleParent
* GetFrom(dom::BrowsingContext
* aBrowsingContext
);
316 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf
) override
;
319 ~DocAccessibleParent();
321 class ProxyEntry
: public PLDHashEntryHdr
{
323 explicit ProxyEntry(const void*) : mProxy(nullptr) {}
324 ProxyEntry(ProxyEntry
&& aOther
) : mProxy(aOther
.mProxy
) {
325 aOther
.mProxy
= nullptr;
327 ~ProxyEntry() { delete mProxy
; }
329 typedef uint64_t KeyType
;
330 typedef const void* KeyTypePointer
;
332 bool KeyEquals(const void* aKey
) const {
333 return mProxy
->ID() == (uint64_t)aKey
;
336 static const void* KeyToPointer(uint64_t aKey
) { return (void*)aKey
; }
338 static PLDHashNumber
HashKey(const void* aKey
) { return (uint64_t)aKey
; }
340 enum { ALLOW_MEMMOVE
= true };
342 RemoteAccessible
* mProxy
;
345 RemoteAccessible
* CreateAcc(const AccessibleData
& aAccData
);
346 void AttachChild(RemoteAccessible
* aParent
, uint32_t aIndex
,
347 RemoteAccessible
* aChild
);
348 [[nodiscard
]] bool CheckDocTree() const;
349 xpcAccessibleGeneric
* GetXPCAccessible(RemoteAccessible
* aProxy
);
351 void FireEvent(RemoteAccessible
* aAcc
, const uint32_t& aType
);
354 * If this Accessible is being moved, prepare it for reuse. Otherwise, it is
355 * being removed, so shut it down.
357 void ShutdownOrPrepareForMove(RemoteAccessible
* aAcc
);
359 nsTArray
<uint64_t> mChildDocs
;
363 // The handle associated with the emulated window that contains this document
364 HWND mEmulatedWindowHandle
;
365 #endif // defined(XP_WIN)
368 * Conceptually this is a map from IDs to proxies, but we store the ID in the
369 * proxy object so we can't use a real map.
371 nsTHashtable
<ProxyEntry
> mAccessibles
;
372 uint64_t mPendingShowChild
= 0;
373 uint64_t mPendingShowParent
= 0;
374 uint32_t mPendingShowIndex
= 0;
375 nsTHashSet
<uint64_t> mMovingIDs
;
378 bool mTopLevelInContentProcess
;
380 RefPtr
<dom::CanonicalBrowsingContext
> mBrowsingContext
;
382 nsTHashSet
<RefPtr
<dom::BrowserBridgeParent
>> mPendingOOPChildDocs
;
386 int32_t mCaretOffset
;
387 bool mIsCaretAtEndOfLine
;
388 nsTArray
<TextRangeData
> mTextSelections
;
390 static uint64_t sMaxDocID
;
391 static nsTHashMap
<nsUint64HashKey
, DocAccessibleParent
*>& LiveDocs() {
392 static nsTHashMap
<nsUint64HashKey
, DocAccessibleParent
*> sLiveDocs
;
398 } // namespace mozilla