/src/mozilla-central/accessible/ipc/DocAccessibleParent.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | #include "DocAccessibleParent.h" |
8 | | #include "mozilla/a11y/Platform.h" |
9 | | #include "mozilla/dom/TabParent.h" |
10 | | #include "xpcAccessibleDocument.h" |
11 | | #include "xpcAccEvents.h" |
12 | | #include "nsAccUtils.h" |
13 | | #include "nsCoreUtils.h" |
14 | | |
15 | | #if defined(XP_WIN) |
16 | | #include "AccessibleWrap.h" |
17 | | #include "Compatibility.h" |
18 | | #include "mozilla/mscom/PassthruProxy.h" |
19 | | #include "mozilla/mscom/Ptr.h" |
20 | | #include "nsWinUtils.h" |
21 | | #include "RootAccessible.h" |
22 | | #endif |
23 | | |
24 | | namespace mozilla { |
25 | | namespace a11y { |
26 | | uint64_t DocAccessibleParent::sMaxDocID = 0; |
27 | | |
28 | | mozilla::ipc::IPCResult |
29 | | DocAccessibleParent::RecvShowEvent(const ShowEventData& aData, |
30 | | const bool& aFromUser) |
31 | 0 | { |
32 | 0 | if (mShutdown) |
33 | 0 | return IPC_OK(); |
34 | 0 | |
35 | 0 | MOZ_ASSERT(CheckDocTree()); |
36 | 0 |
|
37 | 0 | if (aData.NewTree().IsEmpty()) { |
38 | 0 | return IPC_FAIL(this, "No children being added"); |
39 | 0 | } |
40 | 0 |
|
41 | 0 | ProxyAccessible* parent = GetAccessible(aData.ID()); |
42 | 0 |
|
43 | 0 | // XXX This should really never happen, but sometimes we fail to fire the |
44 | 0 | // required show events. |
45 | 0 | if (!parent) { |
46 | 0 | NS_ERROR("adding child to unknown accessible"); |
47 | | #ifdef DEBUG |
48 | | return IPC_FAIL(this, "unknown parent accessible"); |
49 | | #else |
50 | 0 | return IPC_OK(); |
51 | 0 | #endif |
52 | 0 | } |
53 | 0 |
|
54 | 0 | uint32_t newChildIdx = aData.Idx(); |
55 | 0 | if (newChildIdx > parent->ChildrenCount()) { |
56 | 0 | NS_ERROR("invalid index to add child at"); |
57 | | #ifdef DEBUG |
58 | | return IPC_FAIL(this, "invalid index"); |
59 | | #else |
60 | 0 | return IPC_OK(); |
61 | 0 | #endif |
62 | 0 | } |
63 | 0 |
|
64 | 0 | uint32_t consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx); |
65 | 0 | MOZ_ASSERT(consumed == aData.NewTree().Length()); |
66 | 0 |
|
67 | 0 | // XXX This shouldn't happen, but if we failed to add children then the below |
68 | 0 | // is pointless and can crash. |
69 | 0 | if (!consumed) { |
70 | 0 | return IPC_FAIL(this, "failed to add children"); |
71 | 0 | } |
72 | 0 |
|
73 | | #ifdef DEBUG |
74 | | for (uint32_t i = 0; i < consumed; i++) { |
75 | | uint64_t id = aData.NewTree()[i].ID(); |
76 | | MOZ_ASSERT(mAccessibles.GetEntry(id)); |
77 | | } |
78 | | #endif |
79 | | |
80 | 0 | MOZ_ASSERT(CheckDocTree()); |
81 | 0 |
|
82 | 0 | // Just update, no events. |
83 | 0 | if (aData.EventSuppressed()) { |
84 | 0 | return IPC_OK(); |
85 | 0 | } |
86 | 0 |
|
87 | 0 | ProxyAccessible* target = parent->ChildAt(newChildIdx); |
88 | 0 | ProxyShowHideEvent(target, parent, true, aFromUser); |
89 | 0 |
|
90 | 0 | if (!nsCoreUtils::AccEventObserversExist()) { |
91 | 0 | return IPC_OK(); |
92 | 0 | } |
93 | 0 |
|
94 | 0 | uint32_t type = nsIAccessibleEvent::EVENT_SHOW; |
95 | 0 | xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target); |
96 | 0 | xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this); |
97 | 0 | nsINode* node = nullptr; |
98 | 0 | RefPtr<xpcAccEvent> event = new xpcAccEvent(type, xpcAcc, doc, node, |
99 | 0 | aFromUser); |
100 | 0 | nsCoreUtils::DispatchAccEvent(std::move(event)); |
101 | 0 |
|
102 | 0 | return IPC_OK(); |
103 | 0 | } |
104 | | |
105 | | uint32_t |
106 | | DocAccessibleParent::AddSubtree(ProxyAccessible* aParent, |
107 | | const nsTArray<a11y::AccessibleData>& aNewTree, |
108 | | uint32_t aIdx, uint32_t aIdxInParent) |
109 | 0 | { |
110 | 0 | if (aNewTree.Length() <= aIdx) { |
111 | 0 | NS_ERROR("bad index in serialized tree!"); |
112 | 0 | return 0; |
113 | 0 | } |
114 | 0 |
|
115 | 0 | const AccessibleData& newChild = aNewTree[aIdx]; |
116 | 0 |
|
117 | 0 | if (mAccessibles.Contains(newChild.ID())) { |
118 | 0 | NS_ERROR("ID already in use"); |
119 | 0 | return 0; |
120 | 0 | } |
121 | 0 |
|
122 | 0 | ProxyAccessible* newProxy = new ProxyAccessible( |
123 | 0 | newChild.ID(), aParent, this, newChild.Role(), newChild.Interfaces()); |
124 | 0 |
|
125 | 0 | aParent->AddChildAt(aIdxInParent, newProxy); |
126 | 0 | mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy; |
127 | 0 | ProxyCreated(newProxy, newChild.Interfaces()); |
128 | 0 |
|
129 | | #if defined(XP_WIN) |
130 | | WrapperFor(newProxy)->SetID(newChild.MsaaID()); |
131 | | #endif |
132 | |
|
133 | 0 | uint32_t accessibles = 1; |
134 | 0 | uint32_t kids = newChild.ChildrenCount(); |
135 | 0 | for (uint32_t i = 0; i < kids; i++) { |
136 | 0 | uint32_t consumed = AddSubtree(newProxy, aNewTree, aIdx + accessibles, i); |
137 | 0 | if (!consumed) |
138 | 0 | return 0; |
139 | 0 | |
140 | 0 | accessibles += consumed; |
141 | 0 | } |
142 | 0 |
|
143 | 0 | MOZ_ASSERT(newProxy->ChildrenCount() == kids); |
144 | 0 |
|
145 | 0 | return accessibles; |
146 | 0 | } |
147 | | |
148 | | mozilla::ipc::IPCResult |
149 | | DocAccessibleParent::RecvHideEvent(const uint64_t& aRootID, |
150 | | const bool& aFromUser) |
151 | 0 | { |
152 | 0 | if (mShutdown) |
153 | 0 | return IPC_OK(); |
154 | 0 | |
155 | 0 | MOZ_ASSERT(CheckDocTree()); |
156 | 0 |
|
157 | 0 | // We shouldn't actually need this because mAccessibles shouldn't have an |
158 | 0 | // entry for the document itself, but it doesn't hurt to be explicit. |
159 | 0 | if (!aRootID) { |
160 | 0 | return IPC_FAIL(this, "Trying to hide entire document?"); |
161 | 0 | } |
162 | 0 |
|
163 | 0 | ProxyEntry* rootEntry = mAccessibles.GetEntry(aRootID); |
164 | 0 | if (!rootEntry) { |
165 | 0 | NS_ERROR("invalid root being removed!"); |
166 | 0 | return IPC_OK(); |
167 | 0 | } |
168 | 0 |
|
169 | 0 | ProxyAccessible* root = rootEntry->mProxy; |
170 | 0 | if (!root) { |
171 | 0 | NS_ERROR("invalid root being removed!"); |
172 | 0 | return IPC_OK(); |
173 | 0 | } |
174 | 0 |
|
175 | 0 | ProxyAccessible* parent = root->Parent(); |
176 | 0 | ProxyShowHideEvent(root, parent, false, aFromUser); |
177 | 0 |
|
178 | 0 | RefPtr<xpcAccHideEvent> event = nullptr; |
179 | 0 | if (nsCoreUtils::AccEventObserversExist()) { |
180 | 0 | uint32_t type = nsIAccessibleEvent::EVENT_HIDE; |
181 | 0 | xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(root); |
182 | 0 | xpcAccessibleGeneric* xpcParent = GetXPCAccessible(parent); |
183 | 0 | ProxyAccessible* next = root->NextSibling(); |
184 | 0 | xpcAccessibleGeneric* xpcNext = next ? GetXPCAccessible(next) : nullptr; |
185 | 0 | ProxyAccessible* prev = root->PrevSibling(); |
186 | 0 | xpcAccessibleGeneric* xpcPrev = prev ? GetXPCAccessible(prev) : nullptr; |
187 | 0 | xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this); |
188 | 0 | nsINode* node = nullptr; |
189 | 0 | event = new xpcAccHideEvent(type, xpcAcc, doc, node, aFromUser, xpcParent, |
190 | 0 | xpcNext, xpcPrev); |
191 | 0 | } |
192 | 0 |
|
193 | 0 | parent->RemoveChild(root); |
194 | 0 | root->Shutdown(); |
195 | 0 |
|
196 | 0 | MOZ_ASSERT(CheckDocTree()); |
197 | 0 |
|
198 | 0 | if (event) { |
199 | 0 | nsCoreUtils::DispatchAccEvent(std::move(event)); |
200 | 0 | } |
201 | 0 |
|
202 | 0 | return IPC_OK(); |
203 | 0 | } |
204 | | |
205 | | mozilla::ipc::IPCResult |
206 | | DocAccessibleParent::RecvEvent(const uint64_t& aID, const uint32_t& aEventType) |
207 | 0 | { |
208 | 0 | if (mShutdown) { |
209 | 0 | return IPC_OK(); |
210 | 0 | } |
211 | 0 |
|
212 | 0 | ProxyAccessible* proxy = GetAccessible(aID); |
213 | 0 | if (!proxy) { |
214 | 0 | NS_ERROR("no proxy for event!"); |
215 | 0 | return IPC_OK(); |
216 | 0 | } |
217 | 0 |
|
218 | 0 | ProxyEvent(proxy, aEventType); |
219 | 0 |
|
220 | 0 | if (!nsCoreUtils::AccEventObserversExist()) { |
221 | 0 | return IPC_OK(); |
222 | 0 | } |
223 | 0 |
|
224 | 0 | xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy); |
225 | 0 | xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this); |
226 | 0 | nsINode* node = nullptr; |
227 | 0 | bool fromUser = true; // XXX fix me |
228 | 0 | RefPtr<xpcAccEvent> event = new xpcAccEvent(aEventType, xpcAcc, doc, node, |
229 | 0 | fromUser); |
230 | 0 | nsCoreUtils::DispatchAccEvent(std::move(event)); |
231 | 0 |
|
232 | 0 | return IPC_OK(); |
233 | 0 | } |
234 | | |
235 | | mozilla::ipc::IPCResult |
236 | | DocAccessibleParent::RecvStateChangeEvent(const uint64_t& aID, |
237 | | const uint64_t& aState, |
238 | | const bool& aEnabled) |
239 | 0 | { |
240 | 0 | if (mShutdown) { |
241 | 0 | return IPC_OK(); |
242 | 0 | } |
243 | 0 |
|
244 | 0 | ProxyAccessible* target = GetAccessible(aID); |
245 | 0 | if (!target) { |
246 | 0 | NS_ERROR("we don't know about the target of a state change event!"); |
247 | 0 | return IPC_OK(); |
248 | 0 | } |
249 | 0 |
|
250 | 0 | ProxyStateChangeEvent(target, aState, aEnabled); |
251 | 0 |
|
252 | 0 | if (!nsCoreUtils::AccEventObserversExist()) { |
253 | 0 | return IPC_OK(); |
254 | 0 | } |
255 | 0 |
|
256 | 0 | xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target); |
257 | 0 | xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this); |
258 | 0 | uint32_t type = nsIAccessibleEvent::EVENT_STATE_CHANGE; |
259 | 0 | bool extra; |
260 | 0 | uint32_t state = nsAccUtils::To32States(aState, &extra); |
261 | 0 | bool fromUser = true; // XXX fix this |
262 | 0 | nsINode* node = nullptr; // XXX can we do better? |
263 | 0 | RefPtr<xpcAccStateChangeEvent> event = |
264 | 0 | new xpcAccStateChangeEvent(type, xpcAcc, doc, node, fromUser, state, extra, |
265 | 0 | aEnabled); |
266 | 0 | nsCoreUtils::DispatchAccEvent(std::move(event)); |
267 | 0 |
|
268 | 0 | return IPC_OK(); |
269 | 0 | } |
270 | | |
271 | | mozilla::ipc::IPCResult |
272 | | DocAccessibleParent::RecvCaretMoveEvent(const uint64_t& aID, |
273 | | #if defined(XP_WIN) |
274 | | const LayoutDeviceIntRect& aCaretRect, |
275 | | #endif // defined (XP_WIN) |
276 | | const int32_t& aOffset) |
277 | 0 | { |
278 | 0 | if (mShutdown) { |
279 | 0 | return IPC_OK(); |
280 | 0 | } |
281 | 0 |
|
282 | 0 | ProxyAccessible* proxy = GetAccessible(aID); |
283 | 0 | if (!proxy) { |
284 | 0 | NS_ERROR("unknown caret move event target!"); |
285 | 0 | return IPC_OK(); |
286 | 0 | } |
287 | 0 |
|
288 | | #if defined(XP_WIN) |
289 | | ProxyCaretMoveEvent(proxy, aCaretRect); |
290 | | #else |
291 | 0 | ProxyCaretMoveEvent(proxy, aOffset); |
292 | 0 | #endif |
293 | 0 |
|
294 | 0 | if (!nsCoreUtils::AccEventObserversExist()) { |
295 | 0 | return IPC_OK(); |
296 | 0 | } |
297 | 0 |
|
298 | 0 | xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy); |
299 | 0 | xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this); |
300 | 0 | nsINode* node = nullptr; |
301 | 0 | bool fromUser = true; // XXX fix me |
302 | 0 | uint32_t type = nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED; |
303 | 0 | RefPtr<xpcAccCaretMoveEvent> event = |
304 | 0 | new xpcAccCaretMoveEvent(type, xpcAcc, doc, node, fromUser, aOffset); |
305 | 0 | nsCoreUtils::DispatchAccEvent(std::move(event)); |
306 | 0 |
|
307 | 0 | return IPC_OK(); |
308 | 0 | } |
309 | | |
310 | | mozilla::ipc::IPCResult |
311 | | DocAccessibleParent::RecvTextChangeEvent(const uint64_t& aID, |
312 | | const nsString& aStr, |
313 | | const int32_t& aStart, |
314 | | const uint32_t& aLen, |
315 | | const bool& aIsInsert, |
316 | | const bool& aFromUser) |
317 | 0 | { |
318 | 0 | if (mShutdown) { |
319 | 0 | return IPC_OK(); |
320 | 0 | } |
321 | 0 |
|
322 | 0 | ProxyAccessible* target = GetAccessible(aID); |
323 | 0 | if (!target) { |
324 | 0 | NS_ERROR("text change event target is unknown!"); |
325 | 0 | return IPC_OK(); |
326 | 0 | } |
327 | 0 |
|
328 | 0 | ProxyTextChangeEvent(target, aStr, aStart, aLen, aIsInsert, aFromUser); |
329 | 0 |
|
330 | 0 | if (!nsCoreUtils::AccEventObserversExist()) { |
331 | 0 | return IPC_OK(); |
332 | 0 | } |
333 | 0 |
|
334 | 0 | xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target); |
335 | 0 | xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this); |
336 | 0 | uint32_t type = aIsInsert ? nsIAccessibleEvent::EVENT_TEXT_INSERTED : |
337 | 0 | nsIAccessibleEvent::EVENT_TEXT_REMOVED; |
338 | 0 | nsINode* node = nullptr; |
339 | 0 | RefPtr<xpcAccTextChangeEvent> event = |
340 | 0 | new xpcAccTextChangeEvent(type, xpcAcc, doc, node, aFromUser, aStart, aLen, |
341 | 0 | aIsInsert, aStr); |
342 | 0 | nsCoreUtils::DispatchAccEvent(std::move(event)); |
343 | 0 |
|
344 | 0 | return IPC_OK(); |
345 | 0 | } |
346 | | |
347 | | #if defined(XP_WIN) |
348 | | |
349 | | mozilla::ipc::IPCResult |
350 | | DocAccessibleParent::RecvSyncTextChangeEvent(const uint64_t& aID, |
351 | | const nsString& aStr, |
352 | | const int32_t& aStart, |
353 | | const uint32_t& aLen, |
354 | | const bool& aIsInsert, |
355 | | const bool& aFromUser) |
356 | | { |
357 | | return RecvTextChangeEvent(aID, aStr, aStart, aLen, aIsInsert, aFromUser); |
358 | | } |
359 | | |
360 | | #endif // defined(XP_WIN) |
361 | | |
362 | | mozilla::ipc::IPCResult |
363 | | DocAccessibleParent::RecvSelectionEvent(const uint64_t& aID, |
364 | | const uint64_t& aWidgetID, |
365 | | const uint32_t& aType) |
366 | 0 | { |
367 | 0 | if (mShutdown) { |
368 | 0 | return IPC_OK(); |
369 | 0 | } |
370 | 0 |
|
371 | 0 | ProxyAccessible* target = GetAccessible(aID); |
372 | 0 | ProxyAccessible* widget = GetAccessible(aWidgetID); |
373 | 0 | if (!target || !widget) { |
374 | 0 | NS_ERROR("invalid id in selection event"); |
375 | 0 | return IPC_OK(); |
376 | 0 | } |
377 | 0 |
|
378 | 0 | ProxySelectionEvent(target, widget, aType); |
379 | 0 | if (!nsCoreUtils::AccEventObserversExist()) { |
380 | 0 | return IPC_OK(); |
381 | 0 | } |
382 | 0 | xpcAccessibleGeneric* xpcTarget = GetXPCAccessible(target); |
383 | 0 | xpcAccessibleDocument* xpcDoc = GetAccService()->GetXPCDocument(this); |
384 | 0 | RefPtr<xpcAccEvent> event = new xpcAccEvent(aType, xpcTarget, xpcDoc, |
385 | 0 | nullptr, false); |
386 | 0 | nsCoreUtils::DispatchAccEvent(std::move(event)); |
387 | 0 |
|
388 | 0 | return IPC_OK(); |
389 | 0 | } |
390 | | |
391 | | mozilla::ipc::IPCResult |
392 | | DocAccessibleParent::RecvVirtualCursorChangeEvent(const uint64_t& aID, |
393 | | const uint64_t& aOldPositionID, |
394 | | const int32_t& aOldStartOffset, |
395 | | const int32_t& aOldEndOffset, |
396 | | const uint64_t& aNewPositionID, |
397 | | const int32_t& aNewStartOffset, |
398 | | const int32_t& aNewEndOffset, |
399 | | const int16_t& aReason, |
400 | | const int16_t& aBoundaryType, |
401 | | const bool& aFromUser) |
402 | 0 | { |
403 | 0 | ProxyAccessible* target = GetAccessible(aID); |
404 | 0 | ProxyAccessible* oldPosition = GetAccessible(aOldPositionID); |
405 | 0 | ProxyAccessible* newPosition = GetAccessible(aNewPositionID); |
406 | 0 |
|
407 | 0 | if (!target) { |
408 | 0 | NS_ERROR("no proxy for event!"); |
409 | 0 | return IPC_OK(); |
410 | 0 | } |
411 | 0 |
|
412 | | #if defined(ANDROID) |
413 | | ProxyVirtualCursorChangeEvent(target, |
414 | | oldPosition, aOldStartOffset, aOldEndOffset, |
415 | | newPosition, aNewStartOffset, aNewEndOffset, |
416 | | aReason, aBoundaryType, aFromUser); |
417 | | #endif |
418 | | |
419 | 0 | if (!nsCoreUtils::AccEventObserversExist()) { |
420 | 0 | return IPC_OK(); |
421 | 0 | } |
422 | 0 |
|
423 | 0 | xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this); |
424 | 0 | RefPtr<xpcAccVirtualCursorChangeEvent> event = |
425 | 0 | new xpcAccVirtualCursorChangeEvent(nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED, |
426 | 0 | GetXPCAccessible(target), doc, |
427 | 0 | nullptr, aFromUser, |
428 | 0 | GetXPCAccessible(oldPosition), |
429 | 0 | aOldStartOffset, aOldEndOffset, |
430 | 0 | GetXPCAccessible(newPosition), |
431 | 0 | aNewStartOffset, aNewEndOffset, |
432 | 0 | aBoundaryType, aReason); |
433 | 0 | nsCoreUtils::DispatchAccEvent(std::move(event)); |
434 | 0 |
|
435 | 0 | return IPC_OK(); |
436 | 0 | } |
437 | | |
438 | | mozilla::ipc::IPCResult |
439 | | DocAccessibleParent::RecvScrollingEvent(const uint64_t& aID, |
440 | | const uint64_t& aType, |
441 | | const uint32_t& aScrollX, |
442 | | const uint32_t& aScrollY, |
443 | | const uint32_t& aMaxScrollX, |
444 | | const uint32_t& aMaxScrollY) |
445 | 0 | { |
446 | 0 | ProxyAccessible* target = GetAccessible(aID); |
447 | 0 |
|
448 | 0 | if (!target) { |
449 | 0 | NS_ERROR("no proxy for event!"); |
450 | 0 | return IPC_OK(); |
451 | 0 | } |
452 | 0 |
|
453 | | #if defined(ANDROID) |
454 | | ProxyScrollingEvent(target, aType, aScrollX, aScrollY, aMaxScrollX, aMaxScrollY); |
455 | | #endif |
456 | | |
457 | 0 | if (!nsCoreUtils::AccEventObserversExist()) { |
458 | 0 | return IPC_OK(); |
459 | 0 | } |
460 | 0 |
|
461 | 0 | xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target); |
462 | 0 | xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this); |
463 | 0 | nsINode* node = nullptr; |
464 | 0 | bool fromUser = true; // XXX: Determine if this was from user input. |
465 | 0 | RefPtr<xpcAccScrollingEvent> event = |
466 | 0 | new xpcAccScrollingEvent(aType, xpcAcc, doc, node, fromUser, aScrollX, |
467 | 0 | aScrollY, aMaxScrollX, aMaxScrollY); |
468 | 0 | nsCoreUtils::DispatchAccEvent(std::move(event)); |
469 | 0 |
|
470 | 0 | return IPC_OK(); |
471 | 0 | } |
472 | | |
473 | | mozilla::ipc::IPCResult |
474 | | DocAccessibleParent::RecvRoleChangedEvent(const a11y::role& aRole) |
475 | 0 | { |
476 | 0 | if (mShutdown) { |
477 | 0 | return IPC_OK(); |
478 | 0 | } |
479 | 0 |
|
480 | 0 | mRole = aRole; |
481 | 0 | return IPC_OK(); |
482 | 0 | } |
483 | | |
484 | | mozilla::ipc::IPCResult |
485 | | DocAccessibleParent::RecvBindChildDoc(PDocAccessibleParent* aChildDoc, const uint64_t& aID) |
486 | 0 | { |
487 | 0 | // One document should never directly be the child of another. |
488 | 0 | // We should always have at least an outer doc accessible in between. |
489 | 0 | MOZ_ASSERT(aID); |
490 | 0 | if (!aID) |
491 | 0 | return IPC_FAIL(this, "ID is 0!"); |
492 | 0 | |
493 | 0 | if (mShutdown) { |
494 | 0 | return IPC_OK(); |
495 | 0 | } |
496 | 0 |
|
497 | 0 | MOZ_ASSERT(CheckDocTree()); |
498 | 0 |
|
499 | 0 | auto childDoc = static_cast<DocAccessibleParent*>(aChildDoc); |
500 | 0 | childDoc->Unbind(); |
501 | 0 | ipc::IPCResult result = AddChildDoc(childDoc, aID, false); |
502 | 0 | MOZ_ASSERT(result); |
503 | 0 | MOZ_ASSERT(CheckDocTree()); |
504 | | #ifdef DEBUG |
505 | | if (!result) { |
506 | | return result; |
507 | | } |
508 | | #else |
509 | 0 | result = IPC_OK(); |
510 | 0 | #endif |
511 | 0 |
|
512 | 0 | return result; |
513 | 0 | } |
514 | | |
515 | | ipc::IPCResult |
516 | | DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc, |
517 | | uint64_t aParentID, bool aCreating) |
518 | 0 | { |
519 | 0 | // We do not use GetAccessible here because we want to be sure to not get the |
520 | 0 | // document it self. |
521 | 0 | ProxyEntry* e = mAccessibles.GetEntry(aParentID); |
522 | 0 | if (!e) { |
523 | 0 | return IPC_FAIL(this, "binding to nonexistant proxy!"); |
524 | 0 | } |
525 | 0 |
|
526 | 0 | ProxyAccessible* outerDoc = e->mProxy; |
527 | 0 | MOZ_ASSERT(outerDoc); |
528 | 0 |
|
529 | 0 | // OuterDocAccessibles are expected to only have a document as a child. |
530 | 0 | // However for compatibility we tolerate replacing one document with another |
531 | 0 | // here. |
532 | 0 | if (outerDoc->ChildrenCount() > 1 || |
533 | 0 | (outerDoc->ChildrenCount() == 1 && !outerDoc->ChildAt(0)->IsDoc())) { |
534 | 0 | return IPC_FAIL(this, "binding to proxy that can't be a outerDoc!"); |
535 | 0 | } |
536 | 0 |
|
537 | 0 | if (outerDoc->ChildrenCount() == 1) { |
538 | 0 | MOZ_ASSERT(outerDoc->ChildAt(0)->AsDoc()); |
539 | 0 | outerDoc->ChildAt(0)->AsDoc()->Unbind(); |
540 | 0 | } |
541 | 0 |
|
542 | 0 | aChildDoc->SetParent(outerDoc); |
543 | 0 | outerDoc->SetChildDoc(aChildDoc); |
544 | 0 | mChildDocs.AppendElement(aChildDoc->mActorID); |
545 | 0 | aChildDoc->mParentDoc = mActorID; |
546 | 0 |
|
547 | 0 | if (aCreating) { |
548 | 0 | ProxyCreated(aChildDoc, Interfaces::DOCUMENT | Interfaces::HYPERTEXT); |
549 | 0 | } |
550 | 0 |
|
551 | 0 | return IPC_OK(); |
552 | 0 | } |
553 | | |
554 | | mozilla::ipc::IPCResult |
555 | | DocAccessibleParent::RecvShutdown() |
556 | 0 | { |
557 | 0 | Destroy(); |
558 | 0 |
|
559 | 0 | auto mgr = static_cast<dom::TabParent*>(Manager()); |
560 | 0 | if (!mgr->IsDestroyed()) { |
561 | 0 | if (!PDocAccessibleParent::Send__delete__(this)) { |
562 | 0 | return IPC_FAIL_NO_REASON(mgr); |
563 | 0 | } |
564 | 0 | } |
565 | 0 |
|
566 | 0 | return IPC_OK(); |
567 | 0 | } |
568 | | |
569 | | void |
570 | | DocAccessibleParent::Destroy() |
571 | 0 | { |
572 | 0 | // If we are already shutdown that is because our containing tab parent is |
573 | 0 | // shutting down in which case we don't need to do anything. |
574 | 0 | if (mShutdown) { |
575 | 0 | return; |
576 | 0 | } |
577 | 0 | |
578 | 0 | mShutdown = true; |
579 | 0 |
|
580 | 0 | MOZ_DIAGNOSTIC_ASSERT(LiveDocs().Contains(mActorID)); |
581 | 0 | uint32_t childDocCount = mChildDocs.Length(); |
582 | 0 | for (uint32_t i = 0; i < childDocCount; i++) { |
583 | 0 | for (uint32_t j = i + 1; j < childDocCount; j++) { |
584 | 0 | MOZ_DIAGNOSTIC_ASSERT(mChildDocs[i] != mChildDocs[j]); |
585 | 0 | } |
586 | 0 | } |
587 | 0 |
|
588 | 0 | // XXX This indirection through the hash map of live documents shouldn't be |
589 | 0 | // needed, but be paranoid for now. |
590 | 0 | int32_t actorID = mActorID; |
591 | 0 | for (uint32_t i = childDocCount - 1; i < childDocCount; i--) { |
592 | 0 | DocAccessibleParent* thisDoc = LiveDocs().Get(actorID); |
593 | 0 | MOZ_ASSERT(thisDoc); |
594 | 0 | if (!thisDoc) { |
595 | 0 | return; |
596 | 0 | } |
597 | 0 | |
598 | 0 | thisDoc->ChildDocAt(i)->Destroy(); |
599 | 0 | } |
600 | 0 |
|
601 | 0 | for (auto iter = mAccessibles.Iter(); !iter.Done(); iter.Next()) { |
602 | 0 | MOZ_ASSERT(iter.Get()->mProxy != this); |
603 | 0 | ProxyDestroyed(iter.Get()->mProxy); |
604 | 0 | iter.Remove(); |
605 | 0 | } |
606 | 0 |
|
607 | 0 | DocAccessibleParent* thisDoc = LiveDocs().Get(actorID); |
608 | 0 | MOZ_ASSERT(thisDoc); |
609 | 0 | if (!thisDoc) { |
610 | 0 | return; |
611 | 0 | } |
612 | 0 | |
613 | 0 | // The code above should have already completely cleared these, but to be |
614 | 0 | // extra safe make sure they are cleared here. |
615 | 0 | thisDoc->mAccessibles.Clear(); |
616 | 0 | thisDoc->mChildDocs.Clear(); |
617 | 0 |
|
618 | 0 | DocManager::NotifyOfRemoteDocShutdown(thisDoc); |
619 | 0 | thisDoc = LiveDocs().Get(actorID); |
620 | 0 | MOZ_ASSERT(thisDoc); |
621 | 0 | if (!thisDoc) { |
622 | 0 | return; |
623 | 0 | } |
624 | 0 | |
625 | 0 | ProxyDestroyed(thisDoc); |
626 | 0 | thisDoc = LiveDocs().Get(actorID); |
627 | 0 | MOZ_ASSERT(thisDoc); |
628 | 0 | if (!thisDoc) { |
629 | 0 | return; |
630 | 0 | } |
631 | 0 | |
632 | 0 | if (DocAccessibleParent* parentDoc = thisDoc->ParentDoc()) |
633 | 0 | parentDoc->RemoveChildDoc(thisDoc); |
634 | 0 | else if (IsTopLevel()) |
635 | 0 | GetAccService()->RemoteDocShutdown(this); |
636 | 0 | } |
637 | | |
638 | | DocAccessibleParent* |
639 | | DocAccessibleParent::ParentDoc() const |
640 | 0 | { |
641 | 0 | if (mParentDoc == kNoParentDoc) { |
642 | 0 | return nullptr; |
643 | 0 | } |
644 | 0 | |
645 | 0 | return LiveDocs().Get(mParentDoc); |
646 | 0 | } |
647 | | |
648 | | bool |
649 | | DocAccessibleParent::CheckDocTree() const |
650 | 0 | { |
651 | 0 | size_t childDocs = mChildDocs.Length(); |
652 | 0 | for (size_t i = 0; i < childDocs; i++) { |
653 | 0 | const DocAccessibleParent* childDoc = ChildDocAt(i); |
654 | 0 | if (!childDoc || childDoc->ParentDoc() != this) |
655 | 0 | return false; |
656 | 0 | |
657 | 0 | if (!childDoc->CheckDocTree()) { |
658 | 0 | return false; |
659 | 0 | } |
660 | 0 | } |
661 | 0 |
|
662 | 0 | return true; |
663 | 0 | } |
664 | | |
665 | | xpcAccessibleGeneric* |
666 | | DocAccessibleParent::GetXPCAccessible(ProxyAccessible* aProxy) |
667 | 0 | { |
668 | 0 | xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this); |
669 | 0 | MOZ_ASSERT(doc); |
670 | 0 |
|
671 | 0 | return doc->GetXPCAccessible(aProxy); |
672 | 0 | } |
673 | | |
674 | | #if defined(XP_WIN) |
675 | | void |
676 | | DocAccessibleParent::MaybeInitWindowEmulation() |
677 | | { |
678 | | if (!nsWinUtils::IsWindowEmulationStarted()) { |
679 | | return; |
680 | | } |
681 | | |
682 | | // XXX get the bounds from the tabParent instead of poking at accessibles |
683 | | // which might not exist yet. |
684 | | Accessible* outerDoc = OuterDocOfRemoteBrowser(); |
685 | | if (!outerDoc) { |
686 | | return; |
687 | | } |
688 | | |
689 | | RootAccessible* rootDocument = outerDoc->RootAccessible(); |
690 | | MOZ_ASSERT(rootDocument); |
691 | | |
692 | | bool isActive = true; |
693 | | nsIntRect rect(CW_USEDEFAULT, CW_USEDEFAULT, 0, 0); |
694 | | if (Compatibility::IsDolphin()) { |
695 | | rect = Bounds(); |
696 | | nsIntRect rootRect = rootDocument->Bounds(); |
697 | | rect.MoveToX(rootRect.X() - rect.X()); |
698 | | rect.MoveToY(rect.Y() - rootRect.Y()); |
699 | | |
700 | | auto tab = static_cast<dom::TabParent*>(Manager()); |
701 | | tab->GetDocShellIsActive(&isActive); |
702 | | } |
703 | | |
704 | | nsWinUtils::NativeWindowCreateProc onCreate([this](HWND aHwnd) -> void { |
705 | | IDispatchHolder hWndAccHolder; |
706 | | |
707 | | ::SetPropW(aHwnd, kPropNameDocAccParent, reinterpret_cast<HANDLE>(this)); |
708 | | |
709 | | SetEmulatedWindowHandle(aHwnd); |
710 | | |
711 | | RefPtr<IAccessible> hwndAcc; |
712 | | if (SUCCEEDED(::AccessibleObjectFromWindow(aHwnd, OBJID_WINDOW, |
713 | | IID_IAccessible, |
714 | | getter_AddRefs(hwndAcc)))) { |
715 | | RefPtr<IDispatch> wrapped(mscom::PassthruProxy::Wrap<IDispatch>(WrapNotNull(hwndAcc))); |
716 | | hWndAccHolder.Set(IDispatchHolder::COMPtrType(mscom::ToProxyUniquePtr(std::move(wrapped)))); |
717 | | } |
718 | | |
719 | | Unused << SendEmulatedWindow(reinterpret_cast<uintptr_t>(mEmulatedWindowHandle), |
720 | | hWndAccHolder); |
721 | | }); |
722 | | |
723 | | HWND parentWnd = reinterpret_cast<HWND>(rootDocument->GetNativeWindow()); |
724 | | DebugOnly<HWND> hWnd = nsWinUtils::CreateNativeWindow(kClassNameTabContent, |
725 | | parentWnd, |
726 | | rect.X(), rect.Y(), |
727 | | rect.Width(), rect.Height(), |
728 | | isActive, &onCreate); |
729 | | MOZ_ASSERT(hWnd); |
730 | | } |
731 | | |
732 | | /** |
733 | | * @param aCOMProxy COM Proxy to the document in the content process. |
734 | | */ |
735 | | void |
736 | | DocAccessibleParent::SendParentCOMProxy() |
737 | | { |
738 | | // Make sure that we're not racing with a tab shutdown |
739 | | auto tab = static_cast<dom::TabParent*>(Manager()); |
740 | | MOZ_ASSERT(tab); |
741 | | if (tab->IsDestroyed()) { |
742 | | return; |
743 | | } |
744 | | |
745 | | Accessible* outerDoc = OuterDocOfRemoteBrowser(); |
746 | | if (!outerDoc) { |
747 | | return; |
748 | | } |
749 | | |
750 | | RefPtr<IAccessible> nativeAcc; |
751 | | outerDoc->GetNativeInterface(getter_AddRefs(nativeAcc)); |
752 | | MOZ_ASSERT(nativeAcc); |
753 | | |
754 | | RefPtr<IDispatch> wrapped(mscom::PassthruProxy::Wrap<IDispatch>(WrapNotNull(nativeAcc))); |
755 | | |
756 | | IDispatchHolder::COMPtrType ptr(mscom::ToProxyUniquePtr(std::move(wrapped))); |
757 | | IDispatchHolder holder(std::move(ptr)); |
758 | | if (!PDocAccessibleParent::SendParentCOMProxy(holder)) { |
759 | | return; |
760 | | } |
761 | | |
762 | | #if defined(MOZ_CONTENT_SANDBOX) |
763 | | mParentProxyStream = holder.GetPreservedStream(); |
764 | | #endif // defined(MOZ_CONTENT_SANDBOX) |
765 | | } |
766 | | |
767 | | void |
768 | | DocAccessibleParent::SetEmulatedWindowHandle(HWND aWindowHandle) |
769 | | { |
770 | | if (!aWindowHandle && mEmulatedWindowHandle && IsTopLevel()) { |
771 | | ::DestroyWindow(mEmulatedWindowHandle); |
772 | | } |
773 | | mEmulatedWindowHandle = aWindowHandle; |
774 | | } |
775 | | |
776 | | mozilla::ipc::IPCResult |
777 | | DocAccessibleParent::RecvGetWindowedPluginIAccessible( |
778 | | const WindowsHandle& aHwnd, IAccessibleHolder* aPluginCOMProxy) |
779 | | { |
780 | | #if defined(MOZ_CONTENT_SANDBOX) |
781 | | // We don't actually want the accessible object for aHwnd, but rather the |
782 | | // one that belongs to its child (see HTMLWin32ObjectAccessible). |
783 | | HWND childWnd = ::GetWindow(reinterpret_cast<HWND>(aHwnd), GW_CHILD); |
784 | | if (!childWnd) { |
785 | | // We're seeing this in the wild - the plugin is windowed but we no longer |
786 | | // have a window. |
787 | | return IPC_OK(); |
788 | | } |
789 | | |
790 | | IAccessible* rawAccPlugin = nullptr; |
791 | | HRESULT hr = ::AccessibleObjectFromWindow(childWnd, OBJID_WINDOW, |
792 | | IID_IAccessible, |
793 | | (void**)&rawAccPlugin); |
794 | | if (FAILED(hr)) { |
795 | | // This might happen if the plugin doesn't handle WM_GETOBJECT properly. |
796 | | // We should not consider that a failure. |
797 | | return IPC_OK(); |
798 | | } |
799 | | |
800 | | aPluginCOMProxy->Set(IAccessibleHolder::COMPtrType(rawAccPlugin)); |
801 | | |
802 | | return IPC_OK(); |
803 | | #else |
804 | | return IPC_FAIL(this, "Message unsupported in this build configuration"); |
805 | | #endif |
806 | | } |
807 | | |
808 | | mozilla::ipc::IPCResult |
809 | | DocAccessibleParent::RecvFocusEvent(const uint64_t& aID, |
810 | | const LayoutDeviceIntRect& aCaretRect) |
811 | | { |
812 | | if (mShutdown) { |
813 | | return IPC_OK(); |
814 | | } |
815 | | |
816 | | ProxyAccessible* proxy = GetAccessible(aID); |
817 | | if (!proxy) { |
818 | | NS_ERROR("no proxy for event!"); |
819 | | return IPC_OK(); |
820 | | } |
821 | | |
822 | | ProxyFocusEvent(proxy, aCaretRect); |
823 | | |
824 | | if (!nsCoreUtils::AccEventObserversExist()) { |
825 | | return IPC_OK(); |
826 | | } |
827 | | |
828 | | xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy); |
829 | | xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this); |
830 | | nsINode* node = nullptr; |
831 | | bool fromUser = true; // XXX fix me |
832 | | RefPtr<xpcAccEvent> event = new xpcAccEvent(nsIAccessibleEvent::EVENT_FOCUS, |
833 | | xpcAcc, doc, node, fromUser); |
834 | | nsCoreUtils::DispatchAccEvent(std::move(event)); |
835 | | |
836 | | return IPC_OK(); |
837 | | } |
838 | | |
839 | | #endif // defined(XP_WIN) |
840 | | |
841 | | } // a11y |
842 | | } // mozilla |