Bug 1890277: part 4) Add CSPParser support for the `trusted-types` directive, guarded...
[gecko.git] / accessible / ipc / DocAccessibleParent.cpp
blob1c96bd38cd46195ba17b78cee3cd7fe469e568a4
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/. */
7 #include "ARIAMap.h"
8 #include "CachedTableAccessible.h"
9 #include "DocAccessibleParent.h"
10 #include "mozilla/a11y/Platform.h"
11 #include "mozilla/Components.h" // for mozilla::components
12 #include "mozilla/dom/BrowserBridgeParent.h"
13 #include "mozilla/dom/BrowserParent.h"
14 #include "mozilla/dom/CanonicalBrowsingContext.h"
15 #include "nsAccessibilityService.h"
16 #include "xpcAccessibleDocument.h"
17 #include "xpcAccEvents.h"
18 #include "nsAccUtils.h"
19 #include "nsIIOService.h"
20 #include "TextRange.h"
21 #include "Relation.h"
22 #include "RootAccessible.h"
24 #if defined(XP_WIN)
25 # include "Compatibility.h"
26 # include "nsWinUtils.h"
27 #endif
29 #if defined(ANDROID)
30 # define ACQUIRE_ANDROID_LOCK \
31 MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor());
32 #else
33 # define ACQUIRE_ANDROID_LOCK \
34 do { \
35 } while (0);
36 #endif
38 namespace mozilla {
40 namespace a11y {
41 uint64_t DocAccessibleParent::sMaxDocID = 0;
43 DocAccessibleParent::DocAccessibleParent()
44 : RemoteAccessible(this),
45 mParentDoc(kNoParentDoc),
46 #if defined(XP_WIN)
47 mEmulatedWindowHandle(nullptr),
48 #endif // defined(XP_WIN)
49 mTopLevel(false),
50 mTopLevelInContentProcess(false),
51 mShutdown(false),
52 mFocus(0),
53 mCaretId(0),
54 mCaretOffset(-1),
55 mIsCaretAtEndOfLine(false) {
56 sMaxDocID++;
57 mActorID = sMaxDocID;
58 MOZ_ASSERT(!LiveDocs().Get(mActorID));
59 LiveDocs().InsertOrUpdate(mActorID, this);
62 DocAccessibleParent::~DocAccessibleParent() {
63 UnregisterWeakMemoryReporter(this);
64 LiveDocs().Remove(mActorID);
65 MOZ_ASSERT(mChildDocs.Length() == 0);
66 MOZ_ASSERT(!ParentDoc());
69 already_AddRefed<DocAccessibleParent> DocAccessibleParent::New() {
70 RefPtr<DocAccessibleParent> dap(new DocAccessibleParent());
71 // We need to do this with a non-zero reference count. The easiest way is to
72 // do it in this static method and hide the constructor.
73 RegisterWeakMemoryReporter(dap);
74 return dap.forget();
77 void DocAccessibleParent::SetBrowsingContext(
78 dom::CanonicalBrowsingContext* aBrowsingContext) {
79 mBrowsingContext = aBrowsingContext;
82 mozilla::ipc::IPCResult DocAccessibleParent::RecvShowEvent(
83 nsTArray<AccessibleData>&& aNewTree, const bool& aEventSuppressed,
84 const bool& aComplete, const bool& aFromUser) {
85 ACQUIRE_ANDROID_LOCK
86 if (mShutdown) return IPC_OK();
88 MOZ_ASSERT(CheckDocTree());
90 if (aNewTree.IsEmpty()) {
91 return IPC_FAIL(this, "No children being added");
94 RemoteAccessible* root = nullptr;
95 RemoteAccessible* rootParent = nullptr;
96 RemoteAccessible* lastParent = this;
97 uint64_t lastParentID = 0;
98 for (const auto& accData : aNewTree) {
99 RemoteAccessible* parent = accData.ParentID() == lastParentID
100 ? lastParent
101 : GetAccessible(accData.ParentID());
102 // XXX This should really never happen, but sometimes we fail to fire the
103 // required show events.
104 if (!parent) {
105 NS_ERROR("adding child to unknown accessible");
106 #ifdef DEBUG
107 return IPC_FAIL(this, "unknown parent accessible");
108 #else
109 return IPC_OK();
110 #endif
113 uint32_t childIdx = accData.IndexInParent();
114 if (childIdx > parent->ChildCount()) {
115 NS_ERROR("invalid index to add child at");
116 #ifdef DEBUG
117 return IPC_FAIL(this, "invalid index");
118 #else
119 return IPC_OK();
120 #endif
123 RemoteAccessible* child = CreateAcc(accData);
124 if (!child) {
125 // This shouldn't happen.
126 return IPC_FAIL(this, "failed to add children");
128 if (!root && !mPendingShowChild) {
129 // This is the first Accessible, which is the root of the shown subtree.
130 root = child;
131 rootParent = parent;
133 // If this show event has been split across multiple messages and this is
134 // not the last message, don't attach the shown root to the tree yet.
135 // Otherwise, clients might crawl the incomplete subtree and they won't get
136 // mutation events for the remaining pieces.
137 if (aComplete || root != child) {
138 AttachChild(parent, childIdx, child);
142 MOZ_ASSERT(CheckDocTree());
144 if (!aComplete && !mPendingShowChild) {
145 // This is the first message for a show event split across multiple
146 // messages. Save the show target for subsequent messages and return.
147 const auto& accData = aNewTree[0];
148 mPendingShowChild = accData.ID();
149 mPendingShowParent = accData.ParentID();
150 mPendingShowIndex = accData.IndexInParent();
151 return IPC_OK();
153 if (!aComplete) {
154 // This show event has been split into multiple messages, but this is
155 // neither the first nor the last message. There's nothing more to do here.
156 return IPC_OK();
158 MOZ_ASSERT(aComplete);
159 if (mPendingShowChild) {
160 // This is the last message for a show event split across multiple
161 // messages. Retrieve the saved show target, attach it to the tree and fire
162 // an event if appropriate.
163 rootParent = GetAccessible(mPendingShowParent);
164 MOZ_ASSERT(rootParent);
165 root = GetAccessible(mPendingShowChild);
166 MOZ_ASSERT(root);
167 AttachChild(rootParent, mPendingShowIndex, root);
168 mPendingShowChild = 0;
169 mPendingShowParent = 0;
170 mPendingShowIndex = 0;
173 // Just update, no events.
174 if (aEventSuppressed) {
175 return IPC_OK();
178 PlatformShowHideEvent(root, rootParent, true, aFromUser);
180 if (nsCOMPtr<nsIObserverService> obsService =
181 services::GetObserverService()) {
182 obsService->NotifyObservers(nullptr, NS_ACCESSIBLE_CACHE_TOPIC, nullptr);
185 if (!nsCoreUtils::AccEventObserversExist()) {
186 return IPC_OK();
189 uint32_t type = nsIAccessibleEvent::EVENT_SHOW;
190 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(root);
191 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
192 nsINode* node = nullptr;
193 RefPtr<xpcAccEvent> event =
194 new xpcAccEvent(type, xpcAcc, doc, node, aFromUser);
195 nsCoreUtils::DispatchAccEvent(std::move(event));
197 return IPC_OK();
200 RemoteAccessible* DocAccessibleParent::CreateAcc(
201 const AccessibleData& aAccData) {
202 RemoteAccessible* newProxy;
203 if ((newProxy = GetAccessible(aAccData.ID()))) {
204 // This is a move. Reuse the Accessible; don't destroy it.
205 MOZ_ASSERT(!newProxy->RemoteParent());
206 return newProxy;
209 if (!aria::IsRoleMapIndexValid(aAccData.RoleMapEntryIndex())) {
210 MOZ_ASSERT_UNREACHABLE("Invalid role map entry index");
211 return nullptr;
214 newProxy = new RemoteAccessible(aAccData.ID(), this, aAccData.Role(),
215 aAccData.Type(), aAccData.GenericTypes(),
216 aAccData.RoleMapEntryIndex());
217 mAccessibles.PutEntry(aAccData.ID())->mProxy = newProxy;
219 if (RefPtr<AccAttributes> fields = aAccData.CacheFields()) {
220 newProxy->ApplyCache(CacheUpdateType::Initial, fields);
223 return newProxy;
226 void DocAccessibleParent::AttachChild(RemoteAccessible* aParent,
227 uint32_t aIndex,
228 RemoteAccessible* aChild) {
229 aParent->AddChildAt(aIndex, aChild);
230 aChild->SetParent(aParent);
231 // ProxyCreated might have already been called if aChild is being moved.
232 if (!aChild->GetWrapper()) {
233 ProxyCreated(aChild);
235 if (aChild->IsTableCell()) {
236 CachedTableAccessible::Invalidate(aChild);
238 if (aChild->IsOuterDoc()) {
239 // We can only do this after ProxyCreated is called because it will fire an
240 // event on aChild.
241 mPendingOOPChildDocs.RemoveIf([&](dom::BrowserBridgeParent* bridge) {
242 MOZ_ASSERT(bridge->GetBrowserParent(),
243 "Pending BrowserBridgeParent should be alive");
244 if (bridge->GetEmbedderAccessibleId() != aChild->ID()) {
245 return false;
247 MOZ_ASSERT(bridge->GetEmbedderAccessibleDoc() == this);
248 if (DocAccessibleParent* childDoc = bridge->GetDocAccessibleParent()) {
249 AddChildDoc(childDoc, aChild->ID(), false);
251 return true;
256 void DocAccessibleParent::ShutdownOrPrepareForMove(RemoteAccessible* aAcc) {
257 // Children might be removed or moved. Handle them the same way. We do this
258 // before checking the moving IDs set in order to ensure that we handle moved
259 // descendants properly. Avoid descending into the children of outer documents
260 // for moves since they are added and removed differently to normal children.
261 if (!aAcc->IsOuterDoc()) {
262 // Even if some children are kept, those will be re-attached when we handle
263 // the show event. For now, clear all of them by moving them to a temporary.
264 auto children{std::move(aAcc->mChildren)};
265 for (RemoteAccessible* child : children) {
266 ShutdownOrPrepareForMove(child);
270 const uint64_t id = aAcc->ID();
271 if (!mMovingIDs.Contains(id)) {
272 // This Accessible is being removed.
273 aAcc->Shutdown();
274 return;
276 // This is a move. Moves are sent as a hide and then a show, but for a move,
277 // we want to keep the Accessible alive for reuse later.
278 if (aAcc->IsTable() || aAcc->IsTableCell()) {
279 // For table cells, it's important that we do this before the parent is
280 // cleared because CachedTableAccessible::Invalidate needs the ancestry.
281 CachedTableAccessible::Invalidate(aAcc);
283 if (aAcc->IsHyperText()) {
284 aAcc->InvalidateCachedHyperTextOffsets();
286 aAcc->SetParent(nullptr);
287 mMovingIDs.EnsureRemoved(id);
290 mozilla::ipc::IPCResult DocAccessibleParent::RecvHideEvent(
291 const uint64_t& aRootID, const bool& aFromUser) {
292 ACQUIRE_ANDROID_LOCK
293 if (mShutdown) return IPC_OK();
295 MOZ_ASSERT(CheckDocTree());
297 // We shouldn't actually need this because mAccessibles shouldn't have an
298 // entry for the document itself, but it doesn't hurt to be explicit.
299 if (!aRootID) {
300 return IPC_FAIL(this, "Trying to hide entire document?");
303 ProxyEntry* rootEntry = mAccessibles.GetEntry(aRootID);
304 if (!rootEntry) {
305 NS_ERROR("invalid root being removed!");
306 return IPC_OK();
309 RemoteAccessible* root = rootEntry->mProxy;
310 if (!root) {
311 NS_ERROR("invalid root being removed!");
312 return IPC_OK();
315 RemoteAccessible* parent = root->RemoteParent();
316 PlatformShowHideEvent(root, parent, false, aFromUser);
318 RefPtr<xpcAccHideEvent> event = nullptr;
319 if (nsCoreUtils::AccEventObserversExist()) {
320 uint32_t type = nsIAccessibleEvent::EVENT_HIDE;
321 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(root);
322 xpcAccessibleGeneric* xpcParent = GetXPCAccessible(parent);
323 RemoteAccessible* next = root->RemoteNextSibling();
324 xpcAccessibleGeneric* xpcNext = next ? GetXPCAccessible(next) : nullptr;
325 RemoteAccessible* prev = root->RemotePrevSibling();
326 xpcAccessibleGeneric* xpcPrev = prev ? GetXPCAccessible(prev) : nullptr;
327 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
328 nsINode* node = nullptr;
329 event = new xpcAccHideEvent(type, xpcAcc, doc, node, aFromUser, xpcParent,
330 xpcNext, xpcPrev);
333 parent->RemoveChild(root);
334 ShutdownOrPrepareForMove(root);
336 MOZ_ASSERT(CheckDocTree());
338 if (event) {
339 nsCoreUtils::DispatchAccEvent(std::move(event));
342 return IPC_OK();
345 mozilla::ipc::IPCResult DocAccessibleParent::RecvEvent(
346 const uint64_t& aID, const uint32_t& aEventType) {
347 ACQUIRE_ANDROID_LOCK
348 if (mShutdown) {
349 return IPC_OK();
351 if (aEventType == 0 || aEventType >= nsIAccessibleEvent::EVENT_LAST_ENTRY) {
352 MOZ_ASSERT_UNREACHABLE("Invalid event");
353 return IPC_FAIL(this, "Invalid event");
356 RemoteAccessible* remote = GetAccessible(aID);
357 if (!remote) {
358 NS_ERROR("no proxy for event!");
359 return IPC_OK();
362 FireEvent(remote, aEventType);
363 return IPC_OK();
366 void DocAccessibleParent::FireEvent(RemoteAccessible* aAcc,
367 const uint32_t& aEventType) {
368 if (aEventType == nsIAccessibleEvent::EVENT_REORDER ||
369 aEventType == nsIAccessibleEvent::EVENT_INNER_REORDER) {
370 uint32_t count = aAcc->ChildCount();
371 for (uint32_t c = 0; c < count; ++c) {
372 aAcc->RemoteChildAt(c)->InvalidateGroupInfo();
374 } else if (aEventType == nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE &&
375 aAcc == this) {
376 // A DocAccessible gets the STALE state while it is still loading, but we
377 // don't fire a state change for that. That state might have been
378 // included in the initial cache push, so clear it here.
379 // We also clear the BUSY state here. Although we do fire a state change
380 // for that, we fire it after doc load complete. It doesn't make sense
381 // for the document to report BUSY after doc load complete and doing so
382 // confuses JAWS.
383 UpdateStateCache(states::STALE | states::BUSY, false);
386 PlatformEvent(aAcc, aEventType);
388 if (!nsCoreUtils::AccEventObserversExist()) {
389 return;
392 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(aAcc);
393 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
394 nsINode* node = nullptr;
395 bool fromUser = true; // XXX fix me
396 RefPtr<xpcAccEvent> event =
397 new xpcAccEvent(aEventType, xpcAcc, doc, node, fromUser);
398 nsCoreUtils::DispatchAccEvent(std::move(event));
401 mozilla::ipc::IPCResult DocAccessibleParent::RecvStateChangeEvent(
402 const uint64_t& aID, const uint64_t& aState, const bool& aEnabled) {
403 ACQUIRE_ANDROID_LOCK
404 if (mShutdown) {
405 return IPC_OK();
408 RemoteAccessible* target = GetAccessible(aID);
409 if (!target) {
410 NS_ERROR("we don't know about the target of a state change event!");
411 return IPC_OK();
414 target->UpdateStateCache(aState, aEnabled);
415 if (nsCOMPtr<nsIObserverService> obsService =
416 services::GetObserverService()) {
417 obsService->NotifyObservers(nullptr, NS_ACCESSIBLE_CACHE_TOPIC, nullptr);
419 PlatformStateChangeEvent(target, aState, aEnabled);
421 if (!nsCoreUtils::AccEventObserversExist()) {
422 return IPC_OK();
425 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
426 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
427 uint32_t type = nsIAccessibleEvent::EVENT_STATE_CHANGE;
428 bool extra;
429 uint32_t state = nsAccUtils::To32States(aState, &extra);
430 bool fromUser = true; // XXX fix this
431 nsINode* node = nullptr; // XXX can we do better?
432 RefPtr<xpcAccStateChangeEvent> event = new xpcAccStateChangeEvent(
433 type, xpcAcc, doc, node, fromUser, state, extra, aEnabled);
434 nsCoreUtils::DispatchAccEvent(std::move(event));
436 return IPC_OK();
439 mozilla::ipc::IPCResult DocAccessibleParent::RecvCaretMoveEvent(
440 const uint64_t& aID, const LayoutDeviceIntRect& aCaretRect,
441 const int32_t& aOffset, const bool& aIsSelectionCollapsed,
442 const bool& aIsAtEndOfLine, const int32_t& aGranularity,
443 const bool& aFromUser) {
444 ACQUIRE_ANDROID_LOCK
445 if (mShutdown) {
446 return IPC_OK();
449 RemoteAccessible* proxy = GetAccessible(aID);
450 if (!proxy) {
451 NS_ERROR("unknown caret move event target!");
452 return IPC_OK();
455 mCaretId = aID;
456 mCaretOffset = aOffset;
457 mIsCaretAtEndOfLine = aIsAtEndOfLine;
458 if (aIsSelectionCollapsed) {
459 // We don't fire selection events for collapsed selections, but we need to
460 // ensure we don't have a stale cached selection; e.g. when selecting
461 // forward and then unselecting backward.
462 mTextSelections.ClearAndRetainStorage();
463 mTextSelections.AppendElement(TextRangeData(aID, aID, aOffset, aOffset));
466 PlatformCaretMoveEvent(proxy, aOffset, aIsSelectionCollapsed, aGranularity,
467 aCaretRect, aFromUser);
469 if (!nsCoreUtils::AccEventObserversExist()) {
470 return IPC_OK();
473 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
474 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
475 nsINode* node = nullptr;
476 bool fromUser = true; // XXX fix me
477 uint32_t type = nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED;
478 RefPtr<xpcAccCaretMoveEvent> event = new xpcAccCaretMoveEvent(
479 type, xpcAcc, doc, node, fromUser, aOffset, aIsSelectionCollapsed,
480 aIsAtEndOfLine, aGranularity);
481 nsCoreUtils::DispatchAccEvent(std::move(event));
483 return IPC_OK();
486 mozilla::ipc::IPCResult DocAccessibleParent::RecvTextChangeEvent(
487 const uint64_t& aID, const nsAString& aStr, const int32_t& aStart,
488 const uint32_t& aLen, const bool& aIsInsert, const bool& aFromUser) {
489 ACQUIRE_ANDROID_LOCK
490 if (mShutdown) {
491 return IPC_OK();
494 RemoteAccessible* target = GetAccessible(aID);
495 if (!target) {
496 NS_ERROR("text change event target is unknown!");
497 return IPC_OK();
500 PlatformTextChangeEvent(target, aStr, aStart, aLen, aIsInsert, aFromUser);
502 if (!nsCoreUtils::AccEventObserversExist()) {
503 return IPC_OK();
506 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
507 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
508 uint32_t type = aIsInsert ? nsIAccessibleEvent::EVENT_TEXT_INSERTED
509 : nsIAccessibleEvent::EVENT_TEXT_REMOVED;
510 nsINode* node = nullptr;
511 RefPtr<xpcAccTextChangeEvent> event = new xpcAccTextChangeEvent(
512 type, xpcAcc, doc, node, aFromUser, aStart, aLen, aIsInsert, aStr);
513 nsCoreUtils::DispatchAccEvent(std::move(event));
515 return IPC_OK();
518 mozilla::ipc::IPCResult DocAccessibleParent::RecvSelectionEvent(
519 const uint64_t& aID, const uint64_t& aWidgetID, const uint32_t& aType) {
520 ACQUIRE_ANDROID_LOCK
521 if (mShutdown) {
522 return IPC_OK();
524 if (aType == 0 || aType >= nsIAccessibleEvent::EVENT_LAST_ENTRY) {
525 MOZ_ASSERT_UNREACHABLE("Invalid event");
526 return IPC_FAIL(this, "Invalid event");
529 RemoteAccessible* target = GetAccessible(aID);
530 RemoteAccessible* widget = GetAccessible(aWidgetID);
531 if (!target || !widget) {
532 NS_ERROR("invalid id in selection event");
533 return IPC_OK();
536 PlatformSelectionEvent(target, widget, aType);
537 if (!nsCoreUtils::AccEventObserversExist()) {
538 return IPC_OK();
540 xpcAccessibleGeneric* xpcTarget = GetXPCAccessible(target);
541 xpcAccessibleDocument* xpcDoc = GetAccService()->GetXPCDocument(this);
542 RefPtr<xpcAccEvent> event =
543 new xpcAccEvent(aType, xpcTarget, xpcDoc, nullptr, false);
544 nsCoreUtils::DispatchAccEvent(std::move(event));
546 return IPC_OK();
549 mozilla::ipc::IPCResult DocAccessibleParent::RecvScrollingEvent(
550 const uint64_t& aID, const uint64_t& aType, const uint32_t& aScrollX,
551 const uint32_t& aScrollY, const uint32_t& aMaxScrollX,
552 const uint32_t& aMaxScrollY) {
553 ACQUIRE_ANDROID_LOCK
554 if (mShutdown) {
555 return IPC_OK();
557 if (aType == 0 || aType >= nsIAccessibleEvent::EVENT_LAST_ENTRY) {
558 MOZ_ASSERT_UNREACHABLE("Invalid event");
559 return IPC_FAIL(this, "Invalid event");
562 RemoteAccessible* target = GetAccessible(aID);
563 if (!target) {
564 NS_ERROR("no proxy for event!");
565 return IPC_OK();
568 #if defined(ANDROID)
569 PlatformScrollingEvent(target, aType, aScrollX, aScrollY, aMaxScrollX,
570 aMaxScrollY);
571 #else
572 PlatformEvent(target, aType);
573 #endif
575 if (!nsCoreUtils::AccEventObserversExist()) {
576 return IPC_OK();
579 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
580 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
581 nsINode* node = nullptr;
582 bool fromUser = true; // XXX: Determine if this was from user input.
583 RefPtr<xpcAccScrollingEvent> event =
584 new xpcAccScrollingEvent(aType, xpcAcc, doc, node, fromUser, aScrollX,
585 aScrollY, aMaxScrollX, aMaxScrollY);
586 nsCoreUtils::DispatchAccEvent(std::move(event));
588 return IPC_OK();
591 mozilla::ipc::IPCResult DocAccessibleParent::RecvCache(
592 const mozilla::a11y::CacheUpdateType& aUpdateType,
593 nsTArray<CacheData>&& aData) {
594 ACQUIRE_ANDROID_LOCK
595 if (mShutdown) {
596 return IPC_OK();
599 for (auto& entry : aData) {
600 RemoteAccessible* remote = GetAccessible(entry.ID());
601 if (!remote) {
602 MOZ_ASSERT_UNREACHABLE("No remote found!");
603 continue;
606 remote->ApplyCache(aUpdateType, entry.Fields());
609 if (nsCOMPtr<nsIObserverService> obsService =
610 services::GetObserverService()) {
611 obsService->NotifyObservers(nullptr, NS_ACCESSIBLE_CACHE_TOPIC, nullptr);
614 return IPC_OK();
617 mozilla::ipc::IPCResult DocAccessibleParent::RecvSelectedAccessiblesChanged(
618 nsTArray<uint64_t>&& aSelectedIDs, nsTArray<uint64_t>&& aUnselectedIDs) {
619 ACQUIRE_ANDROID_LOCK
620 if (mShutdown) {
621 return IPC_OK();
624 for (auto& id : aSelectedIDs) {
625 RemoteAccessible* remote = GetAccessible(id);
626 if (!remote) {
627 MOZ_ASSERT_UNREACHABLE("No remote found!");
628 continue;
631 remote->UpdateStateCache(states::SELECTED, true);
634 for (auto& id : aUnselectedIDs) {
635 RemoteAccessible* remote = GetAccessible(id);
636 if (!remote) {
637 MOZ_ASSERT_UNREACHABLE("No remote found!");
638 continue;
641 remote->UpdateStateCache(states::SELECTED, false);
644 if (nsCOMPtr<nsIObserverService> obsService =
645 services::GetObserverService()) {
646 obsService->NotifyObservers(nullptr, NS_ACCESSIBLE_CACHE_TOPIC, nullptr);
649 return IPC_OK();
652 mozilla::ipc::IPCResult DocAccessibleParent::RecvAccessiblesWillMove(
653 nsTArray<uint64_t>&& aIDs) {
654 for (uint64_t id : aIDs) {
655 mMovingIDs.EnsureInserted(id);
657 return IPC_OK();
660 #if !defined(XP_WIN)
661 mozilla::ipc::IPCResult DocAccessibleParent::RecvAnnouncementEvent(
662 const uint64_t& aID, const nsAString& aAnnouncement,
663 const uint16_t& aPriority) {
664 ACQUIRE_ANDROID_LOCK
665 if (mShutdown) {
666 return IPC_OK();
669 RemoteAccessible* target = GetAccessible(aID);
670 if (!target) {
671 NS_ERROR("no proxy for event!");
672 return IPC_OK();
675 # if defined(ANDROID)
676 PlatformAnnouncementEvent(target, aAnnouncement, aPriority);
677 # endif
679 if (!nsCoreUtils::AccEventObserversExist()) {
680 return IPC_OK();
683 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
684 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
685 RefPtr<xpcAccAnnouncementEvent> event = new xpcAccAnnouncementEvent(
686 nsIAccessibleEvent::EVENT_ANNOUNCEMENT, xpcAcc, doc, nullptr, false,
687 aAnnouncement, aPriority);
688 nsCoreUtils::DispatchAccEvent(std::move(event));
690 return IPC_OK();
692 #endif // !defined(XP_WIN)
694 mozilla::ipc::IPCResult DocAccessibleParent::RecvTextSelectionChangeEvent(
695 const uint64_t& aID, nsTArray<TextRangeData>&& aSelection) {
696 ACQUIRE_ANDROID_LOCK
697 if (mShutdown) {
698 return IPC_OK();
701 RemoteAccessible* target = GetAccessible(aID);
702 if (!target) {
703 NS_ERROR("no proxy for event!");
704 return IPC_OK();
707 mTextSelections.ClearAndRetainStorage();
708 mTextSelections.AppendElements(aSelection);
710 #ifdef MOZ_WIDGET_COCOA
711 AutoTArray<TextRange, 1> ranges;
712 SelectionRanges(&ranges);
713 PlatformTextSelectionChangeEvent(target, ranges);
714 #else
715 PlatformEvent(target, nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED);
716 #endif
718 if (!nsCoreUtils::AccEventObserversExist()) {
719 return IPC_OK();
721 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
722 xpcAccessibleDocument* doc = nsAccessibilityService::GetXPCDocument(this);
723 nsINode* node = nullptr;
724 bool fromUser = true; // XXX fix me
725 RefPtr<xpcAccEvent> event =
726 new xpcAccEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED, xpcAcc,
727 doc, node, fromUser);
728 nsCoreUtils::DispatchAccEvent(std::move(event));
730 return IPC_OK();
733 mozilla::ipc::IPCResult DocAccessibleParent::RecvRoleChangedEvent(
734 const a11y::role& aRole, const uint8_t& aRoleMapEntryIndex) {
735 ACQUIRE_ANDROID_LOCK
736 if (mShutdown) {
737 return IPC_OK();
739 if (!aria::IsRoleMapIndexValid(aRoleMapEntryIndex)) {
740 MOZ_ASSERT_UNREACHABLE("Invalid role map entry index");
741 return IPC_FAIL(this, "Invalid role map entry index");
744 mRole = aRole;
745 mRoleMapEntryIndex = aRoleMapEntryIndex;
747 #ifdef MOZ_WIDGET_COCOA
748 PlatformRoleChangedEvent(this, aRole, aRoleMapEntryIndex);
749 #endif
751 return IPC_OK();
754 mozilla::ipc::IPCResult DocAccessibleParent::RecvBindChildDoc(
755 NotNull<PDocAccessibleParent*> aChildDoc, const uint64_t& aID) {
756 ACQUIRE_ANDROID_LOCK
757 // One document should never directly be the child of another.
758 // We should always have at least an outer doc accessible in between.
759 MOZ_ASSERT(aID);
760 if (!aID) return IPC_FAIL(this, "ID is 0!");
762 if (mShutdown) {
763 return IPC_OK();
766 MOZ_ASSERT(CheckDocTree());
768 auto childDoc = static_cast<DocAccessibleParent*>(aChildDoc.get());
769 childDoc->Unbind();
770 ipc::IPCResult result = AddChildDoc(childDoc, aID, false);
771 MOZ_ASSERT(result);
772 MOZ_ASSERT(CheckDocTree());
773 #ifdef DEBUG
774 if (!result) {
775 return result;
777 #else
778 result = IPC_OK();
779 #endif
781 return result;
784 ipc::IPCResult DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc,
785 uint64_t aParentID,
786 bool aCreating) {
787 // We do not use GetAccessible here because we want to be sure to not get the
788 // document it self.
789 ProxyEntry* e = mAccessibles.GetEntry(aParentID);
790 if (!e) {
791 #ifndef FUZZING_SNAPSHOT
792 // This diagnostic assert and the one down below expect a well-behaved
793 // child process. In IPC fuzzing, we directly fuzz parameters of each
794 // method over IPDL and the asserts are not valid under these conditions.
795 MOZ_DIAGNOSTIC_ASSERT(false, "Binding to nonexistent proxy!");
796 #endif
797 return IPC_FAIL(this, "binding to nonexistant proxy!");
800 RemoteAccessible* outerDoc = e->mProxy;
801 MOZ_ASSERT(outerDoc);
803 // OuterDocAccessibles are expected to only have a document as a child.
804 // However for compatibility we tolerate replacing one document with another
805 // here.
806 if (!outerDoc->IsOuterDoc() || outerDoc->ChildCount() > 1 ||
807 (outerDoc->ChildCount() == 1 && !outerDoc->RemoteChildAt(0)->IsDoc())) {
808 #ifndef FUZZING_SNAPSHOT
809 MOZ_DIAGNOSTIC_ASSERT(false,
810 "Binding to parent that isn't a valid OuterDoc!");
811 #endif
812 return IPC_FAIL(this, "Binding to parent that isn't a valid OuterDoc!");
815 if (outerDoc->ChildCount() == 1) {
816 MOZ_ASSERT(outerDoc->RemoteChildAt(0)->AsDoc());
817 outerDoc->RemoteChildAt(0)->AsDoc()->Unbind();
820 aChildDoc->SetParent(outerDoc);
821 outerDoc->SetChildDoc(aChildDoc);
822 mChildDocs.AppendElement(aChildDoc->mActorID);
823 aChildDoc->mParentDoc = mActorID;
825 if (aCreating) {
826 ProxyCreated(aChildDoc);
829 if (aChildDoc->IsTopLevelInContentProcess()) {
830 // aChildDoc is an embedded document in a different content process to
831 // this document.
832 auto embeddedBrowser =
833 static_cast<dom::BrowserParent*>(aChildDoc->Manager());
834 dom::BrowserBridgeParent* bridge =
835 embeddedBrowser->GetBrowserBridgeParent();
836 if (bridge) {
837 #if defined(XP_WIN)
838 if (nsWinUtils::IsWindowEmulationStarted()) {
839 aChildDoc->SetEmulatedWindowHandle(mEmulatedWindowHandle);
841 #endif // defined(XP_WIN)
842 // We need to fire a reorder event on the outer doc accessible.
843 // For same-process documents, this is fired by the content process, but
844 // this isn't possible when the document is in a different process to its
845 // embedder.
846 // FireEvent fires both OS and XPCOM events.
847 FireEvent(outerDoc, nsIAccessibleEvent::EVENT_REORDER);
851 return IPC_OK();
854 ipc::IPCResult DocAccessibleParent::AddChildDoc(
855 dom::BrowserBridgeParent* aBridge) {
856 MOZ_ASSERT(aBridge->GetEmbedderAccessibleDoc() == this);
857 uint64_t parentId = aBridge->GetEmbedderAccessibleId();
858 MOZ_ASSERT(parentId);
859 if (!mAccessibles.GetEntry(parentId)) {
860 // Sometimes, this gets called before the embedder sends us the
861 // OuterDocAccessible. We must add the child when the OuterDocAccessible
862 // gets created later.
863 mPendingOOPChildDocs.Insert(aBridge);
864 return IPC_OK();
866 return AddChildDoc(aBridge->GetDocAccessibleParent(), parentId,
867 /* aCreating */ false);
870 mozilla::ipc::IPCResult DocAccessibleParent::RecvShutdown() {
871 ACQUIRE_ANDROID_LOCK
872 Destroy();
874 auto mgr = static_cast<dom::BrowserParent*>(Manager());
875 if (!mgr->IsDestroyed()) {
876 if (!PDocAccessibleParent::Send__delete__(this)) {
877 return IPC_FAIL_NO_REASON(mgr);
881 return IPC_OK();
884 void DocAccessibleParent::Destroy() {
885 // If we are already shutdown that is because our containing tab parent is
886 // shutting down in which case we don't need to do anything.
887 if (mShutdown) {
888 return;
891 mShutdown = true;
892 mBrowsingContext = nullptr;
894 #ifdef ANDROID
895 if (FocusMgr() && FocusMgr()->IsFocusedRemoteDoc(this)) {
896 FocusMgr()->SetFocusedRemoteDoc(nullptr);
898 #endif
900 MOZ_DIAGNOSTIC_ASSERT(LiveDocs().Contains(mActorID));
901 uint32_t childDocCount = mChildDocs.Length();
902 for (uint32_t i = 0; i < childDocCount; i++) {
903 for (uint32_t j = i + 1; j < childDocCount; j++) {
904 MOZ_DIAGNOSTIC_ASSERT(mChildDocs[i] != mChildDocs[j]);
908 // XXX This indirection through the hash map of live documents shouldn't be
909 // needed, but be paranoid for now.
910 int32_t actorID = mActorID;
911 for (uint32_t i = childDocCount - 1; i < childDocCount; i--) {
912 DocAccessibleParent* thisDoc = LiveDocs().Get(actorID);
913 MOZ_ASSERT(thisDoc);
914 if (!thisDoc) {
915 return;
918 thisDoc->ChildDocAt(i)->Destroy();
921 for (auto iter = mAccessibles.Iter(); !iter.Done(); iter.Next()) {
922 RemoteAccessible* acc = iter.Get()->mProxy;
923 MOZ_ASSERT(acc != this);
924 if (acc->IsTable()) {
925 CachedTableAccessible::Invalidate(acc);
927 ProxyDestroyed(acc);
928 iter.Remove();
931 DocAccessibleParent* thisDoc = LiveDocs().Get(actorID);
932 MOZ_ASSERT(thisDoc);
933 if (!thisDoc) {
934 return;
937 mChildren.Clear();
938 // The code above should have already completely cleared these, but to be
939 // extra safe make sure they are cleared here.
940 thisDoc->mAccessibles.Clear();
941 thisDoc->mChildDocs.Clear();
943 DocManager::NotifyOfRemoteDocShutdown(thisDoc);
944 thisDoc = LiveDocs().Get(actorID);
945 MOZ_ASSERT(thisDoc);
946 if (!thisDoc) {
947 return;
950 ProxyDestroyed(thisDoc);
951 thisDoc = LiveDocs().Get(actorID);
952 MOZ_ASSERT(thisDoc);
953 if (!thisDoc) {
954 return;
957 if (DocAccessibleParent* parentDoc = thisDoc->ParentDoc()) {
958 parentDoc->RemoveChildDoc(thisDoc);
959 } else if (IsTopLevel()) {
960 GetAccService()->RemoteDocShutdown(this);
964 void DocAccessibleParent::ActorDestroy(ActorDestroyReason aWhy) {
965 MOZ_ASSERT(CheckDocTree());
966 if (!mShutdown) {
967 ACQUIRE_ANDROID_LOCK
968 Destroy();
972 DocAccessibleParent* DocAccessibleParent::ParentDoc() const {
973 if (mParentDoc == kNoParentDoc) {
974 return nullptr;
977 return LiveDocs().Get(mParentDoc);
980 bool DocAccessibleParent::CheckDocTree() const {
981 size_t childDocs = mChildDocs.Length();
982 for (size_t i = 0; i < childDocs; i++) {
983 const DocAccessibleParent* childDoc = ChildDocAt(i);
984 if (!childDoc || childDoc->ParentDoc() != this) return false;
986 if (!childDoc->CheckDocTree()) {
987 return false;
991 return true;
994 xpcAccessibleGeneric* DocAccessibleParent::GetXPCAccessible(
995 RemoteAccessible* aProxy) {
996 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
997 MOZ_ASSERT(doc);
999 return doc->GetAccessible(aProxy);
1002 #if defined(XP_WIN)
1003 void DocAccessibleParent::MaybeInitWindowEmulation() {
1004 if (!nsWinUtils::IsWindowEmulationStarted()) {
1005 return;
1008 // XXX get the bounds from the browserParent instead of poking at accessibles
1009 // which might not exist yet.
1010 LocalAccessible* outerDoc = OuterDocOfRemoteBrowser();
1011 if (!outerDoc) {
1012 return;
1015 RootAccessible* rootDocument = outerDoc->RootAccessible();
1016 MOZ_ASSERT(rootDocument);
1018 bool isActive = true;
1019 LayoutDeviceIntRect rect(CW_USEDEFAULT, CW_USEDEFAULT, 0, 0);
1020 if (Compatibility::IsDolphin()) {
1021 rect = Bounds();
1022 LayoutDeviceIntRect rootRect = rootDocument->Bounds();
1023 rect.MoveToX(rootRect.X() - rect.X());
1024 rect.MoveToY(rect.Y() - rootRect.Y());
1026 auto browserParent = static_cast<dom::BrowserParent*>(Manager());
1027 isActive = browserParent->GetDocShellIsActive();
1030 // onCreate is guaranteed to be called synchronously by
1031 // nsWinUtils::CreateNativeWindow, so this reference isn't really necessary.
1032 // However, static analysis complains without it.
1033 RefPtr<DocAccessibleParent> thisRef = this;
1034 nsWinUtils::NativeWindowCreateProc onCreate([thisRef](HWND aHwnd) -> void {
1035 ::SetPropW(aHwnd, kPropNameDocAccParent,
1036 reinterpret_cast<HANDLE>(thisRef.get()));
1037 thisRef->SetEmulatedWindowHandle(aHwnd);
1040 HWND parentWnd = reinterpret_cast<HWND>(rootDocument->GetNativeWindow());
1041 DebugOnly<HWND> hWnd = nsWinUtils::CreateNativeWindow(
1042 kClassNameTabContent, parentWnd, rect.X(), rect.Y(), rect.Width(),
1043 rect.Height(), isActive, &onCreate);
1044 MOZ_ASSERT(hWnd);
1047 void DocAccessibleParent::SetEmulatedWindowHandle(HWND aWindowHandle) {
1048 if (!aWindowHandle && mEmulatedWindowHandle && IsTopLevel()) {
1049 ::DestroyWindow(mEmulatedWindowHandle);
1051 mEmulatedWindowHandle = aWindowHandle;
1053 #endif // defined(XP_WIN)
1055 mozilla::ipc::IPCResult DocAccessibleParent::RecvFocusEvent(
1056 const uint64_t& aID, const LayoutDeviceIntRect& aCaretRect) {
1057 ACQUIRE_ANDROID_LOCK
1058 if (mShutdown) {
1059 return IPC_OK();
1062 RemoteAccessible* proxy = GetAccessible(aID);
1063 if (!proxy) {
1064 NS_ERROR("no proxy for event!");
1065 return IPC_OK();
1068 #ifdef ANDROID
1069 if (FocusMgr()) {
1070 FocusMgr()->SetFocusedRemoteDoc(this);
1072 #endif
1074 mFocus = aID;
1075 PlatformFocusEvent(proxy, aCaretRect);
1077 if (!nsCoreUtils::AccEventObserversExist()) {
1078 return IPC_OK();
1081 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
1082 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
1083 nsINode* node = nullptr;
1084 bool fromUser = true; // XXX fix me
1085 RefPtr<xpcAccEvent> event = new xpcAccEvent(nsIAccessibleEvent::EVENT_FOCUS,
1086 xpcAcc, doc, node, fromUser);
1087 nsCoreUtils::DispatchAccEvent(std::move(event));
1089 return IPC_OK();
1092 void DocAccessibleParent::SelectionRanges(nsTArray<TextRange>* aRanges) const {
1093 aRanges->SetCapacity(mTextSelections.Length());
1094 for (const auto& data : mTextSelections) {
1095 // Selection ranges should usually be in sync with the tree. However, tree
1096 // and selection updates happen using separate IPDL calls, so it's possible
1097 // for a client selection query to arrive between them. Thus, we validate
1098 // the Accessibles and offsets here.
1099 auto* startAcc =
1100 const_cast<RemoteAccessible*>(GetAccessible(data.StartID()));
1101 auto* endAcc = const_cast<RemoteAccessible*>(GetAccessible(data.EndID()));
1102 if (!startAcc || !endAcc) {
1103 continue;
1105 uint32_t startCount = startAcc->CharacterCount();
1106 if (startCount == 0 ||
1107 data.StartOffset() > static_cast<int32_t>(startCount)) {
1108 continue;
1110 uint32_t endCount = endAcc->CharacterCount();
1111 if (endCount == 0 || data.EndOffset() > static_cast<int32_t>(endCount)) {
1112 continue;
1114 aRanges->AppendElement(TextRange(const_cast<DocAccessibleParent*>(this),
1115 startAcc, data.StartOffset(), endAcc,
1116 data.EndOffset()));
1120 Accessible* DocAccessibleParent::FocusedChild() {
1121 LocalAccessible* outerDoc = OuterDocOfRemoteBrowser();
1122 if (!outerDoc) {
1123 return nullptr;
1126 RootAccessible* rootDocument = outerDoc->RootAccessible();
1127 return rootDocument->FocusedChild();
1130 void DocAccessibleParent::URL(nsACString& aURL) const {
1131 if (!mBrowsingContext) {
1132 return;
1134 nsCOMPtr<nsIURI> uri = mBrowsingContext->GetCurrentURI();
1135 if (!uri) {
1136 return;
1138 // Let's avoid treating too long URI in the main process for avoiding
1139 // memory fragmentation as far as possible.
1140 if (uri->SchemeIs("data") || uri->SchemeIs("blob")) {
1141 return;
1143 nsCOMPtr<nsIIOService> io = mozilla::components::IO::Service();
1144 if (NS_WARN_IF(!io)) {
1145 return;
1147 nsCOMPtr<nsIURI> exposableURI;
1148 if (NS_FAILED(io->CreateExposableURI(uri, getter_AddRefs(exposableURI))) ||
1149 MOZ_UNLIKELY(!exposableURI)) {
1150 return;
1152 exposableURI->GetSpec(aURL);
1155 void DocAccessibleParent::URL(nsAString& aURL) const {
1156 nsAutoCString url;
1157 URL(url);
1158 CopyUTF8toUTF16(url, aURL);
1161 void DocAccessibleParent::MimeType(nsAString& aMime) const {
1162 if (mCachedFields) {
1163 mCachedFields->GetAttribute(CacheKey::MimeType, aMime);
1167 Relation DocAccessibleParent::RelationByType(RelationType aType) const {
1168 // If the accessible is top-level, provide the NODE_CHILD_OF relation so that
1169 // MSAA clients can easily get to true parent instead of getting to oleacc's
1170 // ROLE_WINDOW accessible when window emulation is enabled which will prevent
1171 // us from going up further (because it is system generated and has no idea
1172 // about the hierarchy above it).
1173 if (aType == RelationType::NODE_CHILD_OF && IsTopLevel()) {
1174 return Relation(Parent());
1177 return RemoteAccessible::RelationByType(aType);
1180 DocAccessibleParent* DocAccessibleParent::GetFrom(
1181 dom::BrowsingContext* aBrowsingContext) {
1182 if (!aBrowsingContext) {
1183 return nullptr;
1186 dom::BrowserParent* bp = aBrowsingContext->Canonical()->GetBrowserParent();
1187 if (!bp) {
1188 return nullptr;
1191 const ManagedContainer<PDocAccessibleParent>& docs =
1192 bp->ManagedPDocAccessibleParent();
1193 for (auto* key : docs) {
1194 // Iterate over our docs until we find one with a browsing
1195 // context that matches the one we passed in. Return that
1196 // document.
1197 auto* doc = static_cast<a11y::DocAccessibleParent*>(key);
1198 if (doc->GetBrowsingContext() == aBrowsingContext) {
1199 return doc;
1203 return nullptr;
1206 size_t DocAccessibleParent::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) {
1207 size_t size = 0;
1209 size += RemoteAccessible::SizeOfExcludingThis(aMallocSizeOf);
1211 size += mReverseRelations.ShallowSizeOfExcludingThis(aMallocSizeOf);
1212 for (auto i = mReverseRelations.Iter(); !i.Done(); i.Next()) {
1213 size += i.Data().ShallowSizeOfExcludingThis(aMallocSizeOf);
1214 for (auto j = i.Data().Iter(); !j.Done(); j.Next()) {
1215 size += j.Data().ShallowSizeOfExcludingThis(aMallocSizeOf);
1219 size += mOnScreenAccessibles.ShallowSizeOfExcludingThis(aMallocSizeOf);
1221 size += mChildDocs.ShallowSizeOfExcludingThis(aMallocSizeOf);
1223 size += mAccessibles.ShallowSizeOfExcludingThis(aMallocSizeOf);
1224 for (auto i = mAccessibles.Iter(); !i.Done(); i.Next()) {
1225 size += i.Get()->mProxy->SizeOfIncludingThis(aMallocSizeOf);
1228 size += mPendingOOPChildDocs.ShallowSizeOfExcludingThis(aMallocSizeOf);
1230 // The mTextSelections array contains structs of integers. We can count them
1231 // by counting the size of the array - there's no deep structure here.
1232 size += mTextSelections.ShallowSizeOfExcludingThis(aMallocSizeOf);
1234 return size;
1237 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOfAccessibilityCache);
1239 NS_IMETHODIMP
1240 DocAccessibleParent::CollectReports(nsIHandleReportCallback* aHandleReport,
1241 nsISupports* aData, bool aAnon) {
1242 nsAutoCString path;
1244 if (aAnon) {
1245 path = nsPrintfCString("explicit/a11y/cache(%" PRIu64 ")", mActorID);
1246 } else {
1247 nsCString url;
1248 URL(url);
1249 url.ReplaceChar(
1250 '/', '\\'); // Tell the memory reporter this is not a path seperator.
1251 path = nsPrintfCString("explicit/a11y/cache(%s)", url.get());
1254 aHandleReport->Callback(
1255 /* process */ ""_ns, path, KIND_HEAP, UNITS_BYTES,
1256 SizeOfIncludingThis(MallocSizeOfAccessibilityCache),
1257 nsLiteralCString("Size of the accessability cache for this document."),
1258 aData);
1260 return NS_OK;
1263 NS_IMPL_ISUPPORTS(DocAccessibleParent, nsIMemoryReporter);
1265 } // namespace a11y
1266 } // namespace mozilla