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