Line data Source code
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 "nsWinUtils.h"
19 : #include "RootAccessible.h"
20 : #endif
21 :
22 : namespace mozilla {
23 : namespace a11y {
24 : uint64_t DocAccessibleParent::sMaxDocID = 0;
25 :
26 : mozilla::ipc::IPCResult
27 0 : DocAccessibleParent::RecvShowEvent(const ShowEventData& aData,
28 : const bool& aFromUser)
29 : {
30 0 : if (mShutdown)
31 0 : return IPC_OK();
32 :
33 0 : MOZ_ASSERT(CheckDocTree());
34 :
35 0 : if (aData.NewTree().IsEmpty()) {
36 0 : return IPC_FAIL(this, "No children being added");
37 : }
38 :
39 0 : ProxyAccessible* parent = GetAccessible(aData.ID());
40 :
41 : // XXX This should really never happen, but sometimes we fail to fire the
42 : // required show events.
43 0 : if (!parent) {
44 0 : NS_ERROR("adding child to unknown accessible");
45 : #ifdef DEBUG
46 0 : return IPC_FAIL(this, "unknown parent accessible");
47 : #else
48 : return IPC_OK();
49 : #endif
50 : }
51 :
52 0 : uint32_t newChildIdx = aData.Idx();
53 0 : if (newChildIdx > parent->ChildrenCount()) {
54 0 : NS_ERROR("invalid index to add child at");
55 : #ifdef DEBUG
56 0 : return IPC_FAIL(this, "invalid index");
57 : #else
58 : return IPC_OK();
59 : #endif
60 : }
61 :
62 0 : uint32_t consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx);
63 0 : MOZ_ASSERT(consumed == aData.NewTree().Length());
64 :
65 : // XXX This shouldn't happen, but if we failed to add children then the below
66 : // is pointless and can crash.
67 0 : if (!consumed) {
68 0 : return IPC_FAIL(this, "failed to add children");
69 : }
70 :
71 : #ifdef DEBUG
72 0 : for (uint32_t i = 0; i < consumed; i++) {
73 0 : uint64_t id = aData.NewTree()[i].ID();
74 0 : MOZ_ASSERT(mAccessibles.GetEntry(id));
75 : }
76 : #endif
77 :
78 0 : MOZ_ASSERT(CheckDocTree());
79 :
80 0 : ProxyAccessible* target = parent->ChildAt(newChildIdx);
81 0 : ProxyShowHideEvent(target, parent, true, aFromUser);
82 :
83 0 : if (!nsCoreUtils::AccEventObserversExist()) {
84 0 : return IPC_OK();
85 : }
86 :
87 0 : uint32_t type = nsIAccessibleEvent::EVENT_SHOW;
88 0 : xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
89 0 : xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
90 0 : nsIDOMNode* node = nullptr;
91 : RefPtr<xpcAccEvent> event = new xpcAccEvent(type, xpcAcc, doc, node,
92 0 : aFromUser);
93 0 : nsCoreUtils::DispatchAccEvent(Move(event));
94 :
95 0 : return IPC_OK();
96 : }
97 :
98 : uint32_t
99 0 : DocAccessibleParent::AddSubtree(ProxyAccessible* aParent,
100 : const nsTArray<a11y::AccessibleData>& aNewTree,
101 : uint32_t aIdx, uint32_t aIdxInParent)
102 : {
103 0 : if (aNewTree.Length() <= aIdx) {
104 0 : NS_ERROR("bad index in serialized tree!");
105 0 : return 0;
106 : }
107 :
108 0 : const AccessibleData& newChild = aNewTree[aIdx];
109 0 : if (newChild.Role() > roles::LAST_ROLE) {
110 0 : NS_ERROR("invalid role");
111 0 : return 0;
112 : }
113 :
114 0 : if (mAccessibles.Contains(newChild.ID())) {
115 0 : NS_ERROR("ID already in use");
116 0 : return 0;
117 : }
118 :
119 0 : auto role = static_cast<a11y::role>(newChild.Role());
120 :
121 : ProxyAccessible* newProxy =
122 0 : new ProxyAccessible(newChild.ID(), aParent, this, role,
123 0 : newChild.Interfaces());
124 :
125 0 : aParent->AddChildAt(aIdxInParent, newProxy);
126 0 : mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy;
127 0 : ProxyCreated(newProxy, newChild.Interfaces());
128 :
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 :
140 0 : accessibles += consumed;
141 : }
142 :
143 0 : MOZ_ASSERT(newProxy->ChildrenCount() == kids);
144 :
145 0 : return accessibles;
146 : }
147 :
148 : mozilla::ipc::IPCResult
149 0 : DocAccessibleParent::RecvHideEvent(const uint64_t& aRootID,
150 : const bool& aFromUser)
151 : {
152 0 : if (mShutdown)
153 0 : return IPC_OK();
154 :
155 0 : MOZ_ASSERT(CheckDocTree());
156 :
157 : // We shouldn't actually need this because mAccessibles shouldn't have an
158 : // 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 : }
162 :
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 : }
168 :
169 0 : ProxyAccessible* root = rootEntry->mProxy;
170 0 : if (!root) {
171 0 : NS_ERROR("invalid root being removed!");
172 0 : return IPC_OK();
173 : }
174 :
175 0 : ProxyAccessible* parent = root->Parent();
176 0 : ProxyShowHideEvent(root, parent, false, aFromUser);
177 :
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 : nsIDOMNode* node = nullptr;
189 0 : event = new xpcAccHideEvent(type, xpcAcc, doc, node, aFromUser, xpcParent,
190 0 : xpcNext, xpcPrev);
191 : }
192 :
193 0 : parent->RemoveChild(root);
194 0 : root->Shutdown();
195 :
196 0 : MOZ_ASSERT(CheckDocTree());
197 :
198 0 : if (event) {
199 0 : nsCoreUtils::DispatchAccEvent(Move(event));
200 : }
201 :
202 0 : return IPC_OK();
203 : }
204 :
205 : mozilla::ipc::IPCResult
206 0 : DocAccessibleParent::RecvEvent(const uint64_t& aID, const uint32_t& aEventType)
207 : {
208 0 : if (mShutdown) {
209 0 : return IPC_OK();
210 : }
211 :
212 0 : ProxyAccessible* proxy = GetAccessible(aID);
213 0 : if (!proxy) {
214 0 : NS_ERROR("no proxy for event!");
215 0 : return IPC_OK();
216 : }
217 :
218 0 : ProxyEvent(proxy, aEventType);
219 :
220 0 : if (!nsCoreUtils::AccEventObserversExist()) {
221 0 : return IPC_OK();
222 : }
223 :
224 0 : xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
225 0 : xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
226 0 : nsIDOMNode* node = nullptr;
227 0 : bool fromUser = true; // XXX fix me
228 : RefPtr<xpcAccEvent> event = new xpcAccEvent(aEventType, xpcAcc, doc, node,
229 0 : fromUser);
230 0 : nsCoreUtils::DispatchAccEvent(Move(event));
231 :
232 0 : return IPC_OK();
233 : }
234 :
235 : mozilla::ipc::IPCResult
236 0 : DocAccessibleParent::RecvStateChangeEvent(const uint64_t& aID,
237 : const uint64_t& aState,
238 : const bool& aEnabled)
239 : {
240 0 : if (mShutdown) {
241 0 : return IPC_OK();
242 : }
243 :
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 : }
249 :
250 0 : ProxyStateChangeEvent(target, aState, aEnabled);
251 :
252 0 : if (!nsCoreUtils::AccEventObserversExist()) {
253 0 : return IPC_OK();
254 : }
255 :
256 0 : xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
257 0 : xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
258 0 : uint32_t type = nsIAccessibleEvent::EVENT_STATE_CHANGE;
259 : bool extra;
260 0 : uint32_t state = nsAccUtils::To32States(aState, &extra);
261 0 : bool fromUser = true; // XXX fix this
262 0 : nsIDOMNode* node = nullptr; // XXX can we do better?
263 : RefPtr<xpcAccStateChangeEvent> event =
264 : new xpcAccStateChangeEvent(type, xpcAcc, doc, node, fromUser, state, extra,
265 0 : aEnabled);
266 0 : nsCoreUtils::DispatchAccEvent(Move(event));
267 :
268 0 : return IPC_OK();
269 : }
270 :
271 : mozilla::ipc::IPCResult
272 0 : 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 : {
278 0 : if (mShutdown) {
279 0 : return IPC_OK();
280 : }
281 :
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 : }
287 :
288 : #if defined(XP_WIN)
289 : ProxyCaretMoveEvent(proxy, aCaretRect);
290 : #else
291 0 : ProxyCaretMoveEvent(proxy, aOffset);
292 : #endif
293 :
294 0 : if (!nsCoreUtils::AccEventObserversExist()) {
295 0 : return IPC_OK();
296 : }
297 :
298 0 : xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
299 0 : xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
300 0 : nsIDOMNode* node = nullptr;
301 0 : bool fromUser = true; // XXX fix me
302 0 : uint32_t type = nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED;
303 : RefPtr<xpcAccCaretMoveEvent> event =
304 0 : new xpcAccCaretMoveEvent(type, xpcAcc, doc, node, fromUser, aOffset);
305 0 : nsCoreUtils::DispatchAccEvent(Move(event));
306 :
307 0 : return IPC_OK();
308 : }
309 :
310 : mozilla::ipc::IPCResult
311 0 : 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 : {
318 0 : if (mShutdown) {
319 0 : return IPC_OK();
320 : }
321 :
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 : }
327 :
328 0 : ProxyTextChangeEvent(target, aStr, aStart, aLen, aIsInsert, aFromUser);
329 :
330 0 : if (!nsCoreUtils::AccEventObserversExist()) {
331 0 : return IPC_OK();
332 : }
333 :
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 : nsIDOMNode* node = nullptr;
339 : RefPtr<xpcAccTextChangeEvent> event =
340 0 : new xpcAccTextChangeEvent(type, xpcAcc, doc, node, aFromUser, aStart, aLen,
341 0 : aIsInsert, aStr);
342 0 : nsCoreUtils::DispatchAccEvent(Move(event));
343 :
344 0 : return IPC_OK();
345 : }
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 0 : DocAccessibleParent::RecvSelectionEvent(const uint64_t& aID,
364 : const uint64_t& aWidgetID,
365 : const uint32_t& aType)
366 : {
367 0 : if (mShutdown) {
368 0 : return IPC_OK();
369 : }
370 :
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 : }
377 :
378 0 : ProxySelectionEvent(target, widget, aType);
379 0 : if (!nsCoreUtils::AccEventObserversExist()) {
380 0 : return IPC_OK();
381 : }
382 0 : xpcAccessibleGeneric* xpcTarget = GetXPCAccessible(target);
383 0 : xpcAccessibleDocument* xpcDoc = GetAccService()->GetXPCDocument(this);
384 : RefPtr<xpcAccEvent> event = new xpcAccEvent(aType, xpcTarget, xpcDoc,
385 0 : nullptr, false);
386 0 : nsCoreUtils::DispatchAccEvent(Move(event));
387 :
388 0 : return IPC_OK();
389 : }
390 :
391 : mozilla::ipc::IPCResult
392 0 : DocAccessibleParent::RecvRoleChangedEvent(const uint32_t& aRole)
393 : {
394 0 : if (mShutdown) {
395 0 : return IPC_OK();
396 : }
397 :
398 0 : if (aRole > roles::LAST_ROLE) {
399 0 : return IPC_FAIL(this, "Child sent bad role in RoleChangedEvent");
400 : }
401 :
402 0 : mRole = static_cast<a11y::role>(aRole);
403 0 : return IPC_OK();
404 : }
405 :
406 : mozilla::ipc::IPCResult
407 0 : DocAccessibleParent::RecvBindChildDoc(PDocAccessibleParent* aChildDoc, const uint64_t& aID)
408 : {
409 : // One document should never directly be the child of another.
410 : // We should always have at least an outer doc accessible in between.
411 0 : MOZ_ASSERT(aID);
412 0 : if (!aID)
413 0 : return IPC_FAIL(this, "ID is 0!");
414 :
415 0 : if (mShutdown) {
416 0 : return IPC_OK();
417 : }
418 :
419 0 : MOZ_ASSERT(CheckDocTree());
420 :
421 0 : auto childDoc = static_cast<DocAccessibleParent*>(aChildDoc);
422 0 : childDoc->Unbind();
423 0 : ipc::IPCResult result = AddChildDoc(childDoc, aID, false);
424 0 : MOZ_ASSERT(result);
425 0 : MOZ_ASSERT(CheckDocTree());
426 : #ifdef DEBUG
427 0 : if (!result) {
428 0 : return result;
429 : }
430 : #else
431 : result = IPC_OK();
432 : #endif
433 :
434 0 : return result;
435 : }
436 :
437 : ipc::IPCResult
438 0 : DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc,
439 : uint64_t aParentID, bool aCreating)
440 : {
441 : // We do not use GetAccessible here because we want to be sure to not get the
442 : // document it self.
443 0 : ProxyEntry* e = mAccessibles.GetEntry(aParentID);
444 0 : if (!e) {
445 0 : return IPC_FAIL(this, "binding to nonexistant proxy!");
446 : }
447 :
448 0 : ProxyAccessible* outerDoc = e->mProxy;
449 0 : MOZ_ASSERT(outerDoc);
450 :
451 : // OuterDocAccessibles are expected to only have a document as a child.
452 : // However for compatibility we tolerate replacing one document with another
453 : // here.
454 0 : if (outerDoc->ChildrenCount() > 1 ||
455 0 : (outerDoc->ChildrenCount() == 1 && !outerDoc->ChildAt(0)->IsDoc())) {
456 0 : return IPC_FAIL(this, "binding to proxy that can't be a outerDoc!");
457 : }
458 :
459 0 : if (outerDoc->ChildrenCount() == 1) {
460 0 : MOZ_ASSERT(outerDoc->ChildAt(0)->AsDoc());
461 0 : outerDoc->ChildAt(0)->AsDoc()->Unbind();
462 : }
463 :
464 0 : aChildDoc->SetParent(outerDoc);
465 0 : outerDoc->SetChildDoc(aChildDoc);
466 0 : mChildDocs.AppendElement(aChildDoc->mActorID);
467 0 : aChildDoc->mParentDoc = mActorID;
468 :
469 0 : if (aCreating) {
470 0 : ProxyCreated(aChildDoc, Interfaces::DOCUMENT | Interfaces::HYPERTEXT);
471 : }
472 :
473 0 : return IPC_OK();
474 : }
475 :
476 : mozilla::ipc::IPCResult
477 0 : DocAccessibleParent::RecvShutdown()
478 : {
479 0 : Destroy();
480 :
481 0 : auto mgr = static_cast<dom::TabParent*>(Manager());
482 0 : if (!mgr->IsDestroyed()) {
483 0 : if (!PDocAccessibleParent::Send__delete__(this)) {
484 0 : return IPC_FAIL_NO_REASON(mgr);
485 : }
486 : }
487 :
488 0 : return IPC_OK();
489 : }
490 :
491 : void
492 0 : DocAccessibleParent::Destroy()
493 : {
494 : // If we are already shutdown that is because our containing tab parent is
495 : // shutting down in which case we don't need to do anything.
496 0 : if (mShutdown) {
497 0 : return;
498 : }
499 :
500 0 : mShutdown = true;
501 :
502 0 : MOZ_DIAGNOSTIC_ASSERT(LiveDocs().Contains(mActorID));
503 0 : uint32_t childDocCount = mChildDocs.Length();
504 0 : for (uint32_t i = 0; i < childDocCount; i++) {
505 0 : for (uint32_t j = i + 1; j < childDocCount; j++) {
506 0 : MOZ_DIAGNOSTIC_ASSERT(mChildDocs[i] != mChildDocs[j]);
507 : }
508 : }
509 :
510 : // XXX This indirection through the hash map of live documents shouldn't be
511 : // needed, but be paranoid for now.
512 0 : int32_t actorID = mActorID;
513 0 : for (uint32_t i = childDocCount - 1; i < childDocCount; i--) {
514 0 : DocAccessibleParent* thisDoc = LiveDocs().Get(actorID);
515 0 : MOZ_ASSERT(thisDoc);
516 0 : if (!thisDoc) {
517 0 : return;
518 : }
519 :
520 0 : thisDoc->ChildDocAt(i)->Destroy();
521 : }
522 :
523 0 : for (auto iter = mAccessibles.Iter(); !iter.Done(); iter.Next()) {
524 0 : MOZ_ASSERT(iter.Get()->mProxy != this);
525 0 : ProxyDestroyed(iter.Get()->mProxy);
526 0 : iter.Remove();
527 : }
528 :
529 0 : DocAccessibleParent* thisDoc = LiveDocs().Get(actorID);
530 0 : MOZ_ASSERT(thisDoc);
531 0 : if (!thisDoc) {
532 0 : return;
533 : }
534 :
535 : // The code above should have already completely cleared these, but to be
536 : // extra safe make sure they are cleared here.
537 0 : thisDoc->mAccessibles.Clear();
538 0 : thisDoc->mChildDocs.Clear();
539 :
540 0 : DocManager::NotifyOfRemoteDocShutdown(thisDoc);
541 0 : thisDoc = LiveDocs().Get(actorID);
542 0 : MOZ_ASSERT(thisDoc);
543 0 : if (!thisDoc) {
544 0 : return;
545 : }
546 :
547 0 : ProxyDestroyed(thisDoc);
548 0 : thisDoc = LiveDocs().Get(actorID);
549 0 : MOZ_ASSERT(thisDoc);
550 0 : if (!thisDoc) {
551 0 : return;
552 : }
553 :
554 0 : if (DocAccessibleParent* parentDoc = thisDoc->ParentDoc())
555 0 : parentDoc->RemoveChildDoc(thisDoc);
556 0 : else if (IsTopLevel())
557 0 : GetAccService()->RemoteDocShutdown(this);
558 : }
559 :
560 : DocAccessibleParent*
561 0 : DocAccessibleParent::ParentDoc() const
562 : {
563 0 : if (mParentDoc == kNoParentDoc) {
564 0 : return nullptr;
565 : }
566 :
567 0 : return LiveDocs().Get(mParentDoc);
568 : }
569 :
570 : bool
571 0 : DocAccessibleParent::CheckDocTree() const
572 : {
573 0 : size_t childDocs = mChildDocs.Length();
574 0 : for (size_t i = 0; i < childDocs; i++) {
575 0 : const DocAccessibleParent* childDoc = ChildDocAt(i);
576 0 : if (!childDoc || childDoc->ParentDoc() != this)
577 0 : return false;
578 :
579 0 : if (!childDoc->CheckDocTree()) {
580 0 : return false;
581 : }
582 : }
583 :
584 0 : return true;
585 : }
586 :
587 : xpcAccessibleGeneric*
588 0 : DocAccessibleParent::GetXPCAccessible(ProxyAccessible* aProxy)
589 : {
590 0 : xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
591 0 : MOZ_ASSERT(doc);
592 :
593 0 : return doc->GetXPCAccessible(aProxy);
594 : }
595 :
596 : #if defined(XP_WIN)
597 : void
598 : DocAccessibleParent::MaybeInitWindowEmulation()
599 : {
600 : if (!nsWinUtils::IsWindowEmulationStarted()) {
601 : return;
602 : }
603 :
604 : // XXX get the bounds from the tabParent instead of poking at accessibles
605 : // which might not exist yet.
606 : Accessible* outerDoc = OuterDocOfRemoteBrowser();
607 : if (!outerDoc) {
608 : return;
609 : }
610 :
611 : RootAccessible* rootDocument = outerDoc->RootAccessible();
612 : MOZ_ASSERT(rootDocument);
613 :
614 : bool isActive = true;
615 : nsIntRect rect(CW_USEDEFAULT, CW_USEDEFAULT, 0, 0);
616 : if (Compatibility::IsDolphin()) {
617 : rect = Bounds();
618 : nsIntRect rootRect = rootDocument->Bounds();
619 : rect.x = rootRect.x - rect.x;
620 : rect.y -= rootRect.y;
621 :
622 : auto tab = static_cast<dom::TabParent*>(Manager());
623 : tab->GetDocShellIsActive(&isActive);
624 : }
625 :
626 : IAccessibleHolder hWndAccHolder;
627 : HWND parentWnd = reinterpret_cast<HWND>(rootDocument->GetNativeWindow());
628 : HWND hWnd = nsWinUtils::CreateNativeWindow(kClassNameTabContent,
629 : parentWnd, rect.x, rect.y,
630 : rect.width, rect.height,
631 : isActive);
632 : if (hWnd) {
633 : // Attach accessible document to the emulated native window
634 : ::SetPropW(hWnd, kPropNameDocAccParent, (HANDLE)this);
635 : SetEmulatedWindowHandle(hWnd);
636 : IAccessible* rawHWNDAcc = nullptr;
637 : if (SUCCEEDED(::AccessibleObjectFromWindow(hWnd, OBJID_WINDOW,
638 : IID_IAccessible,
639 : (void**)&rawHWNDAcc))) {
640 : hWndAccHolder.Set(IAccessibleHolder::COMPtrType(rawHWNDAcc));
641 : }
642 : }
643 :
644 : Unused << SendEmulatedWindow(reinterpret_cast<uintptr_t>(mEmulatedWindowHandle),
645 : hWndAccHolder);
646 : }
647 :
648 : /**
649 : * @param aCOMProxy COM Proxy to the document in the content process.
650 : */
651 : void
652 : DocAccessibleParent::SendParentCOMProxy()
653 : {
654 : // Make sure that we're not racing with a tab shutdown
655 : auto tab = static_cast<dom::TabParent*>(Manager());
656 : MOZ_ASSERT(tab);
657 : if (tab->IsDestroyed()) {
658 : return;
659 : }
660 :
661 : Accessible* outerDoc = OuterDocOfRemoteBrowser();
662 : if (!outerDoc) {
663 : return;
664 : }
665 :
666 : IAccessible* rawNative = nullptr;
667 : outerDoc->GetNativeInterface((void**) &rawNative);
668 : MOZ_ASSERT(rawNative);
669 :
670 : IAccessibleHolder::COMPtrType ptr(rawNative);
671 : IAccessibleHolder holder(Move(ptr));
672 : Unused << PDocAccessibleParent::SendParentCOMProxy(holder);
673 : }
674 :
675 : void
676 : DocAccessibleParent::SetEmulatedWindowHandle(HWND aWindowHandle)
677 : {
678 : if (!aWindowHandle && mEmulatedWindowHandle && IsTopLevel()) {
679 : ::DestroyWindow(mEmulatedWindowHandle);
680 : }
681 : mEmulatedWindowHandle = aWindowHandle;
682 : }
683 :
684 : mozilla::ipc::IPCResult
685 : DocAccessibleParent::RecvGetWindowedPluginIAccessible(
686 : const WindowsHandle& aHwnd, IAccessibleHolder* aPluginCOMProxy)
687 : {
688 : #if defined(MOZ_CONTENT_SANDBOX)
689 : // We don't actually want the accessible object for aHwnd, but rather the
690 : // one that belongs to its child (see HTMLWin32ObjectAccessible).
691 : HWND childWnd = ::GetWindow(reinterpret_cast<HWND>(aHwnd), GW_CHILD);
692 : if (!childWnd) {
693 : // We're seeing this in the wild - the plugin is windowed but we no longer
694 : // have a window.
695 : return IPC_OK();
696 : }
697 :
698 : IAccessible* rawAccPlugin = nullptr;
699 : HRESULT hr = ::AccessibleObjectFromWindow(childWnd, OBJID_WINDOW,
700 : IID_IAccessible,
701 : (void**)&rawAccPlugin);
702 : if (FAILED(hr)) {
703 : // This might happen if the plugin doesn't handle WM_GETOBJECT properly.
704 : // We should not consider that a failure.
705 : return IPC_OK();
706 : }
707 :
708 : aPluginCOMProxy->Set(IAccessibleHolder::COMPtrType(rawAccPlugin));
709 :
710 : return IPC_OK();
711 : #else
712 : return IPC_FAIL(this, "Message unsupported in this build configuration");
713 : #endif
714 : }
715 :
716 : mozilla::ipc::IPCResult
717 : DocAccessibleParent::RecvFocusEvent(const uint64_t& aID,
718 : const LayoutDeviceIntRect& aCaretRect)
719 : {
720 : if (mShutdown) {
721 : return IPC_OK();
722 : }
723 :
724 : ProxyAccessible* proxy = GetAccessible(aID);
725 : if (!proxy) {
726 : NS_ERROR("no proxy for event!");
727 : return IPC_OK();
728 : }
729 :
730 : ProxyFocusEvent(proxy, aCaretRect);
731 :
732 : if (!nsCoreUtils::AccEventObserversExist()) {
733 : return IPC_OK();
734 : }
735 :
736 : xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
737 : xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
738 : nsIDOMNode* node = nullptr;
739 : bool fromUser = true; // XXX fix me
740 : RefPtr<xpcAccEvent> event = new xpcAccEvent(nsIAccessibleEvent::EVENT_FOCUS,
741 : xpcAcc, doc, node, fromUser);
742 : nsCoreUtils::DispatchAccEvent(Move(event));
743 :
744 : return IPC_OK();
745 : }
746 :
747 : #endif // defined(XP_WIN)
748 :
749 : } // a11y
750 : } // mozilla
|