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/ProxyAccessible.h" 13 #include "mozilla/Tuple.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 ProxyAccessible, 32 public PDocAccessibleParent { 33 public: 34 NS_INLINE_DECL_REFCOUNTING(DocAccessibleParent); 35 DocAccessibleParent()36 DocAccessibleParent() 37 : ProxyAccessible(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().Put(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) 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 #endif 144 145 mozilla::ipc::IPCResult RecvRoleChangedEvent(const a11y::role& aRole) final; 146 147 virtual mozilla::ipc::IPCResult RecvBindChildDoc( 148 PDocAccessibleParent* aChildDoc, const uint64_t& aID) override; 149 Unbind()150 void Unbind() { 151 if (DocAccessibleParent* parent = ParentDoc()) { 152 parent->RemoveChildDoc(this); 153 } 154 155 SetParent(nullptr); 156 } 157 158 virtual mozilla::ipc::IPCResult RecvShutdown() override; 159 void Destroy(); ActorDestroy(ActorDestroyReason aWhy)160 virtual void ActorDestroy(ActorDestroyReason aWhy) override { 161 MOZ_ASSERT(CheckDocTree()); 162 if (!mShutdown) Destroy(); 163 } 164 165 /* 166 * Return the main processes representation of the parent document (if any) 167 * of the document this object represents. 168 */ 169 DocAccessibleParent* ParentDoc() const; 170 static const uint64_t kNoParentDoc = UINT64_MAX; 171 172 /* 173 * Called when a document in a content process notifies the main process of a 174 * new child document. 175 */ 176 ipc::IPCResult AddChildDoc(DocAccessibleParent* aChildDoc, uint64_t aParentID, 177 bool aCreating = true); 178 179 /* 180 * Called when the document in the content process this object represents 181 * notifies the main process a child document has been removed. 182 */ RemoveChildDoc(DocAccessibleParent * aChildDoc)183 void RemoveChildDoc(DocAccessibleParent* aChildDoc) { 184 ProxyAccessible* parent = aChildDoc->Parent(); 185 MOZ_ASSERT(parent); 186 if (parent) { 187 aChildDoc->Parent()->ClearChildDoc(aChildDoc); 188 } 189 DebugOnly<bool> result = mChildDocs.RemoveElement(aChildDoc->mActorID); 190 aChildDoc->mParentDoc = kNoParentDoc; 191 MOZ_ASSERT(result); 192 } 193 RemoveAccessible(ProxyAccessible * aAccessible)194 void RemoveAccessible(ProxyAccessible* aAccessible) { 195 MOZ_DIAGNOSTIC_ASSERT(mAccessibles.GetEntry(aAccessible->ID())); 196 mAccessibles.RemoveEntry(aAccessible->ID()); 197 } 198 199 /** 200 * Return the accessible for given id. 201 */ GetAccessible(uintptr_t aID)202 ProxyAccessible* GetAccessible(uintptr_t aID) { 203 if (!aID) return this; 204 205 ProxyEntry* e = mAccessibles.GetEntry(aID); 206 return e ? e->mProxy : nullptr; 207 } 208 GetAccessible(uintptr_t aID)209 const ProxyAccessible* GetAccessible(uintptr_t aID) const { 210 return const_cast<DocAccessibleParent*>(this)->GetAccessible(aID); 211 } 212 ChildDocCount()213 size_t ChildDocCount() const { return mChildDocs.Length(); } ChildDocAt(size_t aIdx)214 const DocAccessibleParent* ChildDocAt(size_t aIdx) const { 215 return const_cast<DocAccessibleParent*>(this)->ChildDocAt(aIdx); 216 } ChildDocAt(size_t aIdx)217 DocAccessibleParent* ChildDocAt(size_t aIdx) { 218 return LiveDocs().Get(mChildDocs[aIdx]); 219 } 220 221 #if defined(XP_WIN) 222 void MaybeInitWindowEmulation(); 223 224 /** 225 * Note that an OuterDocAccessible can be created before the 226 * DocAccessibleParent or vice versa. Therefore, this must be conditionally 227 * called when either of these is created. 228 * @param aOuterDoc The OuterDocAccessible to be returned as the parent of 229 * this document. Only GetNativeInterface() is called on this, so it 230 * may be a ProxyAccessibleWrap or similar. 231 */ 232 void SendParentCOMProxy(Accessible* aOuterDoc); 233 234 virtual mozilla::ipc::IPCResult RecvGetWindowedPluginIAccessible( 235 const WindowsHandle& aHwnd, IAccessibleHolder* aPluginCOMProxy) override; 236 237 /** 238 * Set emulated native window handle for a document. 239 * @param aWindowHandle emulated native window handle 240 */ 241 void SetEmulatedWindowHandle(HWND aWindowHandle); GetEmulatedWindowHandle()242 HWND GetEmulatedWindowHandle() const { return mEmulatedWindowHandle; } 243 #endif 244 245 #if !defined(XP_WIN) 246 virtual mozilla::ipc::IPCResult RecvBatch( 247 const uint64_t& aBatchType, nsTArray<BatchData>&& aData) override; 248 249 virtual bool DeallocPDocAccessiblePlatformExtParent( 250 PDocAccessiblePlatformExtParent* aActor) override; 251 252 virtual PDocAccessiblePlatformExtParent* 253 AllocPDocAccessiblePlatformExtParent() override; 254 255 DocAccessiblePlatformExtParent* GetPlatformExtension(); 256 #endif 257 258 /** 259 * If this is an iframe document rendered in a different process to its 260 * embedder, return the DocAccessibleParent and id for the embedder 261 * accessible. Otherwise, return null and 0. 262 */ 263 Tuple<DocAccessibleParent*, uint64_t> GetRemoteEmbedder(); 264 265 private: ~DocAccessibleParent()266 ~DocAccessibleParent() { 267 LiveDocs().Remove(mActorID); 268 MOZ_ASSERT(mChildDocs.Length() == 0); 269 MOZ_ASSERT(!ParentDoc()); 270 } 271 272 class ProxyEntry : public PLDHashEntryHdr { 273 public: ProxyEntry(const void *)274 explicit ProxyEntry(const void*) : mProxy(nullptr) {} ProxyEntry(ProxyEntry && aOther)275 ProxyEntry(ProxyEntry&& aOther) : mProxy(aOther.mProxy) { 276 aOther.mProxy = nullptr; 277 } ~ProxyEntry()278 ~ProxyEntry() { delete mProxy; } 279 280 typedef uint64_t KeyType; 281 typedef const void* KeyTypePointer; 282 KeyEquals(const void * aKey)283 bool KeyEquals(const void* aKey) const { 284 return mProxy->ID() == (uint64_t)aKey; 285 } 286 KeyToPointer(uint64_t aKey)287 static const void* KeyToPointer(uint64_t aKey) { return (void*)aKey; } 288 HashKey(const void * aKey)289 static PLDHashNumber HashKey(const void* aKey) { return (uint64_t)aKey; } 290 291 enum { ALLOW_MEMMOVE = true }; 292 293 ProxyAccessible* mProxy; 294 }; 295 296 uint32_t AddSubtree(ProxyAccessible* aParent, 297 const nsTArray<AccessibleData>& aNewTree, uint32_t aIdx, 298 uint32_t aIdxInParent); 299 [[nodiscard]] bool CheckDocTree() const; 300 xpcAccessibleGeneric* GetXPCAccessible(ProxyAccessible* aProxy); 301 302 nsTArray<uint64_t> mChildDocs; 303 uint64_t mParentDoc; 304 305 #if defined(XP_WIN) 306 // The handle associated with the emulated window that contains this document 307 HWND mEmulatedWindowHandle; 308 309 # if defined(MOZ_SANDBOX) 310 mscom::PreservedStreamPtr mParentProxyStream; 311 mscom::PreservedStreamPtr mDocProxyStream; 312 mscom::PreservedStreamPtr mTopLevelDocProxyStream; 313 # endif // defined(MOZ_SANDBOX) 314 #endif // defined(XP_WIN) 315 316 /* 317 * Conceptually this is a map from IDs to proxies, but we store the ID in the 318 * proxy object so we can't use a real map. 319 */ 320 nsTHashtable<ProxyEntry> mAccessibles; 321 uint64_t mActorID; 322 bool mTopLevel; 323 bool mTopLevelInContentProcess; 324 bool mShutdown; 325 326 struct PendingChildDoc { PendingChildDocPendingChildDoc327 PendingChildDoc(DocAccessibleParent* aChildDoc, uint64_t aParentID) 328 : mChildDoc(aChildDoc), mParentID(aParentID) {} 329 RefPtr<DocAccessibleParent> mChildDoc; 330 uint64_t mParentID; 331 }; 332 // We use nsTArray because there will be very few entries. 333 nsTArray<PendingChildDoc> mPendingChildDocs; 334 335 static uint64_t sMaxDocID; LiveDocs()336 static nsDataHashtable<nsUint64HashKey, DocAccessibleParent*>& LiveDocs() { 337 static nsDataHashtable<nsUint64HashKey, DocAccessibleParent*> sLiveDocs; 338 return sLiveDocs; 339 } 340 }; 341 342 } // namespace a11y 343 } // namespace mozilla 344 345 #endif 346