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 a11y { 20 21 class xpcAccessibleGeneric; 22 23 #if !defined(XP_WIN) 24 class DocAccessiblePlatformExtParent; 25 #endif 26 27 /* 28 * These objects live in the main process and comunicate with and represent 29 * an accessible document in a content process. 30 */ 31 class DocAccessibleParent : public RemoteAccessible, 32 public PDocAccessibleParent { 33 public: 34 NS_INLINE_DECL_REFCOUNTING(DocAccessibleParent); 35 DocAccessibleParent()36 DocAccessibleParent() 37 : RemoteAccessible(this), 38 mParentDoc(kNoParentDoc), 39 #if defined(XP_WIN) 40 mEmulatedWindowHandle(nullptr), 41 #endif // defined(XP_WIN) 42 mTopLevel(false), 43 mTopLevelInContentProcess(false), 44 mShutdown(false) { 45 sMaxDocID++; 46 mActorID = sMaxDocID; 47 MOZ_ASSERT(!LiveDocs().Get(mActorID)); 48 LiveDocs().InsertOrUpdate(mActorID, this); 49 } 50 51 /** 52 * Set this as a top level document; i.e. it is not embedded by another remote 53 * document. This also means it is a top level document in its content 54 * process. 55 * Tab documents are top level documents. 56 */ SetTopLevel()57 void SetTopLevel() { 58 mTopLevel = true; 59 mTopLevelInContentProcess = true; 60 } IsTopLevel()61 bool IsTopLevel() const { return mTopLevel; } 62 63 /** 64 * Set this as a top level document in its content process. 65 * Note that this could be an out-of-process iframe embedded by a remote 66 * embedder document. In that case, IsToplevel() will return false, but 67 * IsTopLevelInContentProcess() will return true. 68 */ SetTopLevelInContentProcess()69 void SetTopLevelInContentProcess() { mTopLevelInContentProcess = true; } IsTopLevelInContentProcess()70 bool IsTopLevelInContentProcess() const { return mTopLevelInContentProcess; } 71 IsShutdown()72 bool IsShutdown() const { return mShutdown; } 73 74 /** 75 * Mark this actor as shutdown without doing any cleanup. This should only 76 * be called on actors that have just been initialized, so probably only from 77 * RecvPDocAccessibleConstructor. 78 */ MarkAsShutdown()79 void MarkAsShutdown() { 80 MOZ_ASSERT(mChildDocs.IsEmpty()); 81 MOZ_ASSERT(mAccessibles.Count() == 0); 82 mShutdown = true; 83 } 84 85 /* 86 * Called when a message from a document in a child process notifies the main 87 * process it is firing an event. 88 */ 89 virtual mozilla::ipc::IPCResult RecvEvent(const uint64_t& aID, 90 const uint32_t& aType) override; 91 92 virtual mozilla::ipc::IPCResult RecvShowEvent(const ShowEventData& aData, 93 const bool& aFromUser) override; 94 virtual mozilla::ipc::IPCResult RecvHideEvent(const uint64_t& aRootID, 95 const bool& aFromUser) override; 96 mozilla::ipc::IPCResult RecvStateChangeEvent(const uint64_t& aID, 97 const uint64_t& aState, 98 const bool& aEnabled) final; 99 100 mozilla::ipc::IPCResult RecvCaretMoveEvent( 101 const uint64_t& aID, 102 #if defined(XP_WIN) 103 const LayoutDeviceIntRect& aCaretRect, 104 #endif 105 const int32_t& aOffset, const bool& aIsSelectionCollapsed) final; 106 107 virtual mozilla::ipc::IPCResult RecvTextChangeEvent( 108 const uint64_t& aID, const nsString& aStr, const int32_t& aStart, 109 const uint32_t& aLen, const bool& aIsInsert, 110 const bool& aFromUser) override; 111 112 #if defined(XP_WIN) 113 virtual mozilla::ipc::IPCResult RecvSyncTextChangeEvent( 114 const uint64_t& aID, const nsString& aStr, const int32_t& aStart, 115 const uint32_t& aLen, const bool& aIsInsert, 116 const bool& aFromUser) override; 117 118 virtual mozilla::ipc::IPCResult RecvFocusEvent( 119 const uint64_t& aID, const LayoutDeviceIntRect& aCaretRect) override; 120 #endif // defined(XP_WIN) 121 122 virtual mozilla::ipc::IPCResult RecvSelectionEvent( 123 const uint64_t& aID, const uint64_t& aWidgetID, 124 const uint32_t& aType) override; 125 126 MOZ_CAN_RUN_SCRIPT_BOUNDARY 127 virtual mozilla::ipc::IPCResult RecvVirtualCursorChangeEvent( 128 const uint64_t& aID, const uint64_t& aOldPositionID, 129 const int32_t& aOldStartOffset, const int32_t& aOldEndOffset, 130 const uint64_t& aNewPositionID, const int32_t& aNewStartOffset, 131 const int32_t& aNewEndOffset, const int16_t& aReason, 132 const int16_t& aBoundaryType, const bool& aFromUser) override; 133 134 virtual mozilla::ipc::IPCResult RecvScrollingEvent( 135 const uint64_t& aID, const uint64_t& aType, const uint32_t& aScrollX, 136 const uint32_t& aScrollY, const uint32_t& aMaxScrollX, 137 const uint32_t& aMaxScrollY) override; 138 139 #if !defined(XP_WIN) 140 virtual mozilla::ipc::IPCResult RecvAnnouncementEvent( 141 const uint64_t& aID, const nsString& aAnnouncement, 142 const uint16_t& aPriority) override; 143 144 virtual mozilla::ipc::IPCResult RecvTextSelectionChangeEvent( 145 const uint64_t& aID, nsTArray<TextRangeData>&& aSelection) override; 146 #endif 147 148 mozilla::ipc::IPCResult RecvRoleChangedEvent(const a11y::role& aRole) final; 149 150 virtual mozilla::ipc::IPCResult RecvBindChildDoc( 151 PDocAccessibleParent* aChildDoc, const uint64_t& aID) override; 152 Unbind()153 void Unbind() { 154 if (DocAccessibleParent* parent = ParentDoc()) { 155 parent->RemoveChildDoc(this); 156 } 157 158 SetParent(nullptr); 159 } 160 161 virtual mozilla::ipc::IPCResult RecvShutdown() override; 162 void Destroy(); ActorDestroy(ActorDestroyReason aWhy)163 virtual void ActorDestroy(ActorDestroyReason aWhy) override { 164 MOZ_ASSERT(CheckDocTree()); 165 if (!mShutdown) Destroy(); 166 } 167 168 /* 169 * Return the main processes representation of the parent document (if any) 170 * of the document this object represents. 171 */ 172 DocAccessibleParent* ParentDoc() const; 173 static const uint64_t kNoParentDoc = UINT64_MAX; 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 RemovePendingOOPChildDoc(dom::BrowserBridgeParent * aBridge)191 void RemovePendingOOPChildDoc(dom::BrowserBridgeParent* aBridge) { 192 mPendingOOPChildDocs.Remove(aBridge); 193 } 194 195 /* 196 * Called when the document in the content process this object represents 197 * notifies the main process a child document has been removed. 198 */ RemoveChildDoc(DocAccessibleParent * aChildDoc)199 void RemoveChildDoc(DocAccessibleParent* aChildDoc) { 200 RemoteAccessible* parent = aChildDoc->RemoteParent(); 201 MOZ_ASSERT(parent); 202 if (parent) { 203 aChildDoc->RemoteParent()->ClearChildDoc(aChildDoc); 204 } 205 DebugOnly<bool> result = mChildDocs.RemoveElement(aChildDoc->mActorID); 206 aChildDoc->mParentDoc = kNoParentDoc; 207 MOZ_ASSERT(result); 208 } 209 RemoveAccessible(RemoteAccessible * aAccessible)210 void RemoveAccessible(RemoteAccessible* aAccessible) { 211 MOZ_DIAGNOSTIC_ASSERT(mAccessibles.GetEntry(aAccessible->ID())); 212 mAccessibles.RemoveEntry(aAccessible->ID()); 213 } 214 215 /** 216 * Return the accessible for given id. 217 */ GetAccessible(uintptr_t aID)218 RemoteAccessible* GetAccessible(uintptr_t aID) { 219 if (!aID) return this; 220 221 ProxyEntry* e = mAccessibles.GetEntry(aID); 222 return e ? e->mProxy : nullptr; 223 } 224 GetAccessible(uintptr_t aID)225 const RemoteAccessible* GetAccessible(uintptr_t aID) const { 226 return const_cast<DocAccessibleParent*>(this)->GetAccessible(aID); 227 } 228 ChildDocCount()229 size_t ChildDocCount() const { return mChildDocs.Length(); } ChildDocAt(size_t aIdx)230 const DocAccessibleParent* ChildDocAt(size_t aIdx) const { 231 return const_cast<DocAccessibleParent*>(this)->ChildDocAt(aIdx); 232 } ChildDocAt(size_t aIdx)233 DocAccessibleParent* ChildDocAt(size_t aIdx) { 234 return LiveDocs().Get(mChildDocs[aIdx]); 235 } 236 237 #if defined(XP_WIN) 238 void MaybeInitWindowEmulation(); 239 240 /** 241 * Note that an OuterDocAccessible can be created before the 242 * DocAccessibleParent or vice versa. Therefore, this must be conditionally 243 * called when either of these is created. 244 * @param aOuterDoc The OuterDocAccessible to be returned as the parent of 245 * this document. Only GetNativeInterface() is called on this, so it 246 * may be a RemoteAccessibleWrap or similar. 247 */ 248 void SendParentCOMProxy(LocalAccessible* aOuterDoc); 249 250 /** 251 * Set emulated native window handle for a document. 252 * @param aWindowHandle emulated native window handle 253 */ 254 void SetEmulatedWindowHandle(HWND aWindowHandle); GetEmulatedWindowHandle()255 HWND GetEmulatedWindowHandle() const { return mEmulatedWindowHandle; } 256 #endif 257 258 #if !defined(XP_WIN) 259 virtual mozilla::ipc::IPCResult RecvBatch( 260 const uint64_t& aBatchType, nsTArray<BatchData>&& aData) override; 261 262 virtual bool DeallocPDocAccessiblePlatformExtParent( 263 PDocAccessiblePlatformExtParent* aActor) override; 264 265 virtual PDocAccessiblePlatformExtParent* 266 AllocPDocAccessiblePlatformExtParent() override; 267 268 DocAccessiblePlatformExtParent* GetPlatformExtension(); 269 #endif 270 271 // Accessible IndexInParent()272 virtual int32_t IndexInParent() const override { 273 if (IsTopLevel() && OuterDocOfRemoteBrowser()) { 274 // An OuterDoc can only have 1 child. 275 return 0; 276 } 277 return RemoteAccessible::IndexInParent(); 278 } 279 280 private: ~DocAccessibleParent()281 ~DocAccessibleParent() { 282 LiveDocs().Remove(mActorID); 283 MOZ_ASSERT(mChildDocs.Length() == 0); 284 MOZ_ASSERT(!ParentDoc()); 285 } 286 287 class ProxyEntry : public PLDHashEntryHdr { 288 public: ProxyEntry(const void *)289 explicit ProxyEntry(const void*) : mProxy(nullptr) {} ProxyEntry(ProxyEntry && aOther)290 ProxyEntry(ProxyEntry&& aOther) : mProxy(aOther.mProxy) { 291 aOther.mProxy = nullptr; 292 } ~ProxyEntry()293 ~ProxyEntry() { delete mProxy; } 294 295 typedef uint64_t KeyType; 296 typedef const void* KeyTypePointer; 297 KeyEquals(const void * aKey)298 bool KeyEquals(const void* aKey) const { 299 return mProxy->ID() == (uint64_t)aKey; 300 } 301 KeyToPointer(uint64_t aKey)302 static const void* KeyToPointer(uint64_t aKey) { return (void*)aKey; } 303 HashKey(const void * aKey)304 static PLDHashNumber HashKey(const void* aKey) { return (uint64_t)aKey; } 305 306 enum { ALLOW_MEMMOVE = true }; 307 308 RemoteAccessible* mProxy; 309 }; 310 311 uint32_t AddSubtree(RemoteAccessible* aParent, 312 const nsTArray<AccessibleData>& aNewTree, uint32_t aIdx, 313 uint32_t aIdxInParent); 314 [[nodiscard]] bool CheckDocTree() const; 315 xpcAccessibleGeneric* GetXPCAccessible(RemoteAccessible* aProxy); 316 317 nsTArray<uint64_t> mChildDocs; 318 uint64_t mParentDoc; 319 320 #if defined(XP_WIN) 321 // The handle associated with the emulated window that contains this document 322 HWND mEmulatedWindowHandle; 323 324 # if defined(MOZ_SANDBOX) 325 mscom::PreservedStreamPtr mParentProxyStream; 326 mscom::PreservedStreamPtr mDocProxyStream; 327 mscom::PreservedStreamPtr mTopLevelDocProxyStream; 328 # endif // defined(MOZ_SANDBOX) 329 #endif // defined(XP_WIN) 330 331 /* 332 * Conceptually this is a map from IDs to proxies, but we store the ID in the 333 * proxy object so we can't use a real map. 334 */ 335 nsTHashtable<ProxyEntry> mAccessibles; 336 uint64_t mActorID; 337 bool mTopLevel; 338 bool mTopLevelInContentProcess; 339 bool mShutdown; 340 341 nsTHashSet<RefPtr<dom::BrowserBridgeParent>> mPendingOOPChildDocs; 342 343 static uint64_t sMaxDocID; LiveDocs()344 static nsTHashMap<nsUint64HashKey, DocAccessibleParent*>& LiveDocs() { 345 static nsTHashMap<nsUint64HashKey, DocAccessibleParent*> sLiveDocs; 346 return sLiveDocs; 347 } 348 }; 349 350 } // namespace a11y 351 } // namespace mozilla 352 353 #endif 354