DocAccessibleParent.h (12994B)
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/. */ 6 7 #ifndef mozilla_a11y_DocAccessibleParent_h 8 #define mozilla_a11y_DocAccessibleParent_h 9 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" 17 18 namespace mozilla { 19 namespace dom { 20 class CanonicalBrowsingContext; 21 } 22 23 namespace a11y { 24 25 class TextRange; 26 class xpcAccessibleGeneric; 27 28 /* 29 * These objects live in the main process and comunicate with and represent 30 * an accessible document in a content process. 31 */ 32 class DocAccessibleParent : public RemoteAccessible, 33 public PDocAccessibleParent, 34 public nsIMemoryReporter { 35 public: 36 NS_DECL_ISUPPORTS 37 NS_DECL_NSIMEMORYREPORTER 38 39 private: 40 DocAccessibleParent(); 41 42 public: 43 static already_AddRefed<DocAccessibleParent> New(); 44 45 /** 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 48 * process. 49 * Tab documents are top level documents. 50 */ 51 void SetTopLevel() { 52 mTopLevel = true; 53 mTopLevelInContentProcess = true; 54 } 55 bool IsTopLevel() const { return mTopLevel; } 56 57 /** 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. 62 */ 63 void SetTopLevelInContentProcess() { mTopLevelInContentProcess = true; } 64 bool IsTopLevelInContentProcess() const { return mTopLevelInContentProcess; } 65 66 /** 67 * Determine whether this is an out-of-process iframe document, embedded by a 68 * remote embedder document. 69 */ 70 bool IsOOPIframeDoc() const { 71 return !mTopLevel && mTopLevelInContentProcess; 72 } 73 74 bool IsShutdown() const { return mShutdown; } 75 76 /** 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. 80 */ 81 void MarkAsShutdown() { 82 MOZ_ASSERT(mChildDocs.IsEmpty()); 83 MOZ_ASSERT(mAccessibles.Count() == 0); 84 MOZ_ASSERT(!mBrowsingContext); 85 mShutdown = true; 86 } 87 88 void SetBrowsingContext(dom::CanonicalBrowsingContext* aBrowsingContext); 89 90 dom::CanonicalBrowsingContext* GetBrowsingContext() const { 91 return mBrowsingContext; 92 } 93 94 /* 95 * Called when a message from a document in a child process notifies the main 96 * process it is firing an event. 97 */ 98 virtual mozilla::ipc::IPCResult RecvEvent(const uint64_t& aID, 99 const uint32_t& aType) override; 100 101 mozilla::ipc::IPCResult RecvStateChangeEvent(const uint64_t& aID, 102 const uint64_t& aState, 103 const bool& aEnabled) final; 104 105 mozilla::ipc::IPCResult RecvCaretMoveEvent( 106 const uint64_t& aID, const LayoutDeviceIntRect& aCaretRect, 107 const int32_t& aOffset, const bool& aIsSelectionCollapsed, 108 const bool& aIsAtEndOfLine, const int32_t& aGranularity, 109 const bool& aFromUser) final; 110 111 virtual mozilla::ipc::IPCResult RecvMutationEvents( 112 nsTArray<MutationEventData>&& aData) override; 113 114 virtual mozilla::ipc::IPCResult RecvRequestAckMutationEvents() override; 115 116 virtual mozilla::ipc::IPCResult RecvFocusEvent( 117 const uint64_t& aID, const LayoutDeviceIntRect& aCaretRect) override; 118 119 virtual mozilla::ipc::IPCResult RecvSelectionEvent( 120 const uint64_t& aID, const uint64_t& aWidgetID, 121 const uint32_t& aType) override; 122 123 virtual mozilla::ipc::IPCResult RecvScrollingEvent( 124 const uint64_t& aID, const uint64_t& aType, const uint32_t& aScrollX, 125 const uint32_t& aScrollY, const uint32_t& aMaxScrollX, 126 const uint32_t& aMaxScrollY) override; 127 128 virtual mozilla::ipc::IPCResult RecvCache( 129 const mozilla::a11y::CacheUpdateType& aUpdateType, 130 nsTArray<CacheData>&& aData) override; 131 132 virtual mozilla::ipc::IPCResult RecvSelectedAccessiblesChanged( 133 nsTArray<uint64_t>&& aSelectedIDs, 134 nsTArray<uint64_t>&& aUnselectedIDs) override; 135 136 virtual mozilla::ipc::IPCResult RecvAccessiblesWillMove( 137 nsTArray<uint64_t>&& aIDs) override; 138 139 #if !defined(XP_WIN) 140 virtual mozilla::ipc::IPCResult RecvAnnouncementEvent( 141 const uint64_t& aID, const nsAString& aAnnouncement, 142 const uint16_t& aPriority) override; 143 #endif 144 145 virtual mozilla::ipc::IPCResult RecvTextSelectionChangeEvent( 146 const uint64_t& aID, nsTArray<TextRangeData>&& aSelection) override; 147 148 mozilla::ipc::IPCResult RecvRoleChangedEvent( 149 const a11y::role& aRole, const uint8_t& aRoleMapEntryIndex) final; 150 151 virtual mozilla::ipc::IPCResult RecvBindChildDoc( 152 NotNull<PDocAccessibleParent*> aChildDoc, const uint64_t& aID) override; 153 154 void Unbind() { 155 if (RemoteAccessible* parent = RemoteParent()) { 156 DocAccessibleParent* parentDoc = parent->Document(); 157 parent->ClearChildDoc(this); 158 DebugOnly<bool> result = parentDoc->mChildDocs.RemoveElement(mActorID); 159 MOZ_ASSERT(result); 160 } 161 162 SetParent(nullptr); 163 } 164 165 virtual mozilla::ipc::IPCResult RecvShutdown() override; 166 void Destroy(); 167 virtual void ActorDestroy(ActorDestroyReason aWhy) override; 168 169 /* 170 * Return the main processes representation of the parent document (if any) 171 * of the document this object represents. 172 */ 173 DocAccessibleParent* ParentDoc() const; 174 175 /** 176 * Called when a document in a content process notifies the main process of a 177 * new child document. 178 * Although this is called internally for OOP child documents, these should be 179 * added via the BrowserBridgeParent version of this method, as the parent id 180 * might not exist yet in that case. 181 */ 182 ipc::IPCResult AddChildDoc(DocAccessibleParent* aChildDoc, uint64_t aParentID, 183 bool aCreating = true); 184 185 /** 186 * Called when a document in a content process notifies the main process of a 187 * new OOP child document. 188 */ 189 ipc::IPCResult AddChildDoc(dom::BrowserBridgeParent* aBridge); 190 191 void RemovePendingOOPChildDoc(dom::BrowserBridgeParent* aBridge) { 192 mPendingOOPChildDocs.Remove(aBridge); 193 } 194 195 void RemoveAccessible(RemoteAccessible* aAccessible) { 196 MOZ_DIAGNOSTIC_ASSERT(mAccessibles.GetEntry(aAccessible->ID())); 197 mAccessibles.RemoveEntry(aAccessible->ID()); 198 } 199 200 /** 201 * Return the accessible for given id. 202 */ 203 RemoteAccessible* GetAccessible(uintptr_t aID) { 204 if (!aID) return this; 205 206 ProxyEntry* e = mAccessibles.GetEntry(aID); 207 return e ? e->mProxy : nullptr; 208 } 209 210 const RemoteAccessible* GetAccessible(uintptr_t aID) const { 211 return const_cast<DocAccessibleParent*>(this)->GetAccessible(aID); 212 } 213 214 size_t ChildDocCount() const { return mChildDocs.Length(); } 215 const DocAccessibleParent* ChildDocAt(size_t aIdx) const { 216 return const_cast<DocAccessibleParent*>(this)->ChildDocAt(aIdx); 217 } 218 DocAccessibleParent* ChildDocAt(size_t aIdx) { 219 return LiveDocs().Get(mChildDocs[aIdx]); 220 } 221 222 #if defined(XP_WIN) 223 void MaybeInitWindowEmulation(); 224 225 /** 226 * Set emulated native window handle for a document. 227 * @param aWindowHandle emulated native window handle 228 */ 229 void SetEmulatedWindowHandle(HWND aWindowHandle); 230 HWND GetEmulatedWindowHandle() const { return mEmulatedWindowHandle; } 231 #endif 232 233 // Accessible 234 virtual Accessible* Parent() const override { 235 if (IsTopLevel()) { 236 return OuterDocOfRemoteBrowser(); 237 } 238 return RemoteParent(); 239 } 240 241 virtual int32_t IndexInParent() const override { 242 if (IsTopLevel() && OuterDocOfRemoteBrowser()) { 243 // An OuterDoc can only have 1 child. 244 return 0; 245 } 246 return RemoteAccessible::IndexInParent(); 247 } 248 249 /** 250 * Get the focused Accessible in this document, if any. 251 */ 252 RemoteAccessible* GetFocusedAcc() const { 253 return const_cast<DocAccessibleParent*>(this)->GetAccessible(mFocus); 254 } 255 256 /** 257 * Get the HyperText Accessible containing the caret and the offset of the 258 * caret within. If there is no caret in this document, returns 259 * {nullptr, -1}. 260 */ 261 std::pair<RemoteAccessible*, int32_t> GetCaret() const { 262 if (mCaretOffset == -1) { 263 return {nullptr, -1}; 264 } 265 RemoteAccessible* acc = 266 const_cast<DocAccessibleParent*>(this)->GetAccessible(mCaretId); 267 if (!acc) { 268 return {nullptr, -1}; 269 } 270 return {acc, mCaretOffset}; 271 } 272 273 bool IsCaretAtEndOfLine() const { return mIsCaretAtEndOfLine; } 274 275 mozilla::LayoutDeviceIntRect GetCachedCaretRect(); 276 277 virtual void SelectionRanges(nsTArray<TextRange>* aRanges) const override; 278 279 virtual Accessible* FocusedChild() override; 280 281 void URL(nsAString& aURL) const; 282 void URL(nsACString& aURL) const; 283 284 void MimeType(nsAString& aURL) const; 285 286 virtual Relation RelationByType(RelationType aType) const override; 287 288 // Tracks cached reverse relations (ie. those not set explicitly by an 289 // attribute like aria-labelledby) for accessibles in this doc. This map is of 290 // the form: {accID, {pointerToRelationDataAddress, [targetAccID, ...]}} 291 nsTHashMap<uint64_t, nsTHashMap<const RelationData*, nsTArray<uint64_t>>> 292 mReverseRelations; 293 294 // Computed from the viewport cache, the accs referenced by these ids 295 // are currently on screen (making any acc not in this list offscreen). 296 nsTHashSet<uint64_t> mOnScreenAccessibles; 297 298 static DocAccessibleParent* GetFrom(dom::BrowsingContext* aBrowsingContext); 299 300 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) override; 301 302 private: 303 ~DocAccessibleParent(); 304 305 class ProxyEntry : public PLDHashEntryHdr { 306 public: 307 explicit ProxyEntry(const void*) : mProxy(nullptr) {} 308 ProxyEntry(ProxyEntry&& aOther) : mProxy(aOther.mProxy) { 309 aOther.mProxy = nullptr; 310 } 311 ~ProxyEntry() { delete mProxy; } 312 313 typedef uint64_t KeyType; 314 typedef const void* KeyTypePointer; 315 316 bool KeyEquals(const void* aKey) const { 317 return mProxy->ID() == (uint64_t)aKey; 318 } 319 320 static const void* KeyToPointer(uint64_t aKey) { return (void*)aKey; } 321 322 static PLDHashNumber HashKey(const void* aKey) { return (uint64_t)aKey; } 323 324 enum { ALLOW_MEMMOVE = true }; 325 326 RemoteAccessible* mProxy; 327 }; 328 329 RemoteAccessible* CreateAcc(const AccessibleData& aAccData); 330 void AttachChild(RemoteAccessible* aParent, uint32_t aIndex, 331 RemoteAccessible* aChild); 332 [[nodiscard]] bool CheckDocTree() const; 333 xpcAccessibleGeneric* GetXPCAccessible(RemoteAccessible* aProxy); 334 335 void FireEvent(RemoteAccessible* aAcc, const uint32_t& aType); 336 337 /** 338 * If this Accessible is being moved, prepare it for reuse. Otherwise, it is 339 * being removed, so shut it down. 340 */ 341 void ShutdownOrPrepareForMove(RemoteAccessible* aAcc); 342 343 mozilla::ipc::IPCResult ProcessShowEvent(nsTArray<AccessibleData>&& aNewTree, 344 const bool& aEventSuppressed, 345 const bool& aComplete, 346 const bool& aFromUser); 347 mozilla::ipc::IPCResult ProcessHideEvent(const uint64_t& aRootID, 348 const bool& aFromUser); 349 mozilla::ipc::IPCResult ProcessTextChangeEvent( 350 const uint64_t& aID, const nsAString& aStr, const int32_t& aStart, 351 const uint32_t& aLen, const bool& aIsInsert, const bool& aFromUser); 352 353 nsTArray<uint64_t> mChildDocs; 354 355 #if defined(XP_WIN) 356 // The handle associated with the emulated window that contains this document 357 HWND mEmulatedWindowHandle; 358 #endif // defined(XP_WIN) 359 360 /* 361 * Conceptually this is a map from IDs to proxies, but we store the ID in the 362 * proxy object so we can't use a real map. 363 */ 364 nsTHashtable<ProxyEntry> mAccessibles; 365 uint64_t mPendingShowChild = 0; 366 uint64_t mPendingShowParent = 0; 367 uint32_t mPendingShowIndex = 0; 368 nsTHashSet<uint64_t> mMovingIDs; 369 uint64_t mActorID; 370 bool mTopLevel; 371 bool mTopLevelInContentProcess; 372 bool mShutdown; 373 RefPtr<dom::CanonicalBrowsingContext> mBrowsingContext; 374 375 nsTHashSet<RefPtr<dom::BrowserBridgeParent>> mPendingOOPChildDocs; 376 377 uint64_t mFocus; 378 uint64_t mCaretId; 379 int32_t mCaretOffset; 380 bool mIsCaretAtEndOfLine; 381 LayoutDeviceIntRect mCaretRect; 382 nsTArray<TextRangeData> mTextSelections; 383 384 static uint64_t sMaxDocID; 385 static nsTHashMap<nsUint64HashKey, DocAccessibleParent*>& LiveDocs() { 386 static nsTHashMap<nsUint64HashKey, DocAccessibleParent*> sLiveDocs; 387 return sLiveDocs; 388 } 389 }; 390 391 } // namespace a11y 392 } // namespace mozilla 393 394 #endif