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