Bug 1814798 - pt 1. Add bool to enable/disable PHC at runtime r=glandium
[gecko.git] / accessible / ipc / DocAccessibleParent.cpp
blob0691b7624ebf6cfe0b1128856b6c05cf9c345450
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();
352 RemoteAccessible* remote = GetAccessible(aID);
353 if (!remote) {
354 NS_ERROR("no proxy for event!");
355 return IPC_OK();
358 FireEvent(remote, aEventType);
359 return IPC_OK();
362 void DocAccessibleParent::FireEvent(RemoteAccessible* aAcc,
363 const uint32_t& aEventType) {
364 if (aEventType == nsIAccessibleEvent::EVENT_REORDER ||
365 aEventType == nsIAccessibleEvent::EVENT_INNER_REORDER) {
366 for (RemoteAccessible* child = aAcc->RemoteFirstChild(); child;
367 child = child->RemoteNextSibling()) {
368 child->InvalidateGroupInfo();
370 } else if (aEventType == nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE &&
371 aAcc == this) {
372 // A DocAccessible gets the STALE state while it is still loading, but we
373 // don't fire a state change for that. That state might have been
374 // included in the initial cache push, so clear it here.
375 // We also clear the BUSY state here. Although we do fire a state change
376 // for that, we fire it after doc load complete. It doesn't make sense
377 // for the document to report BUSY after doc load complete and doing so
378 // confuses JAWS.
379 UpdateStateCache(states::STALE | states::BUSY, false);
382 PlatformEvent(aAcc, aEventType);
384 if (!nsCoreUtils::AccEventObserversExist()) {
385 return;
388 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(aAcc);
389 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
390 nsINode* node = nullptr;
391 bool fromUser = true; // XXX fix me
392 RefPtr<xpcAccEvent> event =
393 new xpcAccEvent(aEventType, xpcAcc, doc, node, fromUser);
394 nsCoreUtils::DispatchAccEvent(std::move(event));
397 mozilla::ipc::IPCResult DocAccessibleParent::RecvStateChangeEvent(
398 const uint64_t& aID, const uint64_t& aState, const bool& aEnabled) {
399 ACQUIRE_ANDROID_LOCK
400 if (mShutdown) {
401 return IPC_OK();
404 RemoteAccessible* target = GetAccessible(aID);
405 if (!target) {
406 NS_ERROR("we don't know about the target of a state change event!");
407 return IPC_OK();
410 target->UpdateStateCache(aState, aEnabled);
411 if (nsCOMPtr<nsIObserverService> obsService =
412 services::GetObserverService()) {
413 obsService->NotifyObservers(nullptr, NS_ACCESSIBLE_CACHE_TOPIC, nullptr);
415 PlatformStateChangeEvent(target, aState, aEnabled);
417 if (!nsCoreUtils::AccEventObserversExist()) {
418 return IPC_OK();
421 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
422 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
423 uint32_t type = nsIAccessibleEvent::EVENT_STATE_CHANGE;
424 bool extra;
425 uint32_t state = nsAccUtils::To32States(aState, &extra);
426 bool fromUser = true; // XXX fix this
427 nsINode* node = nullptr; // XXX can we do better?
428 RefPtr<xpcAccStateChangeEvent> event = new xpcAccStateChangeEvent(
429 type, xpcAcc, doc, node, fromUser, state, extra, aEnabled);
430 nsCoreUtils::DispatchAccEvent(std::move(event));
432 return IPC_OK();
435 mozilla::ipc::IPCResult DocAccessibleParent::RecvCaretMoveEvent(
436 const uint64_t& aID, const LayoutDeviceIntRect& aCaretRect,
437 const int32_t& aOffset, const bool& aIsSelectionCollapsed,
438 const bool& aIsAtEndOfLine, const int32_t& aGranularity) {
439 ACQUIRE_ANDROID_LOCK
440 if (mShutdown) {
441 return IPC_OK();
444 RemoteAccessible* proxy = GetAccessible(aID);
445 if (!proxy) {
446 NS_ERROR("unknown caret move event target!");
447 return IPC_OK();
450 mCaretId = aID;
451 mCaretOffset = aOffset;
452 mIsCaretAtEndOfLine = aIsAtEndOfLine;
453 if (aIsSelectionCollapsed) {
454 // We don't fire selection events for collapsed selections, but we need to
455 // ensure we don't have a stale cached selection; e.g. when selecting
456 // forward and then unselecting backward.
457 mTextSelections.ClearAndRetainStorage();
458 mTextSelections.AppendElement(TextRangeData(aID, aID, aOffset, aOffset));
461 PlatformCaretMoveEvent(proxy, aOffset, aIsSelectionCollapsed, aGranularity,
462 aCaretRect);
464 if (!nsCoreUtils::AccEventObserversExist()) {
465 return IPC_OK();
468 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
469 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
470 nsINode* node = nullptr;
471 bool fromUser = true; // XXX fix me
472 uint32_t type = nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED;
473 RefPtr<xpcAccCaretMoveEvent> event = new xpcAccCaretMoveEvent(
474 type, xpcAcc, doc, node, fromUser, aOffset, aIsSelectionCollapsed,
475 aIsAtEndOfLine, aGranularity);
476 nsCoreUtils::DispatchAccEvent(std::move(event));
478 return IPC_OK();
481 mozilla::ipc::IPCResult DocAccessibleParent::RecvTextChangeEvent(
482 const uint64_t& aID, const nsAString& aStr, const int32_t& aStart,
483 const uint32_t& aLen, const bool& aIsInsert, const bool& aFromUser) {
484 ACQUIRE_ANDROID_LOCK
485 if (mShutdown) {
486 return IPC_OK();
489 RemoteAccessible* target = GetAccessible(aID);
490 if (!target) {
491 NS_ERROR("text change event target is unknown!");
492 return IPC_OK();
495 PlatformTextChangeEvent(target, aStr, aStart, aLen, aIsInsert, aFromUser);
497 if (!nsCoreUtils::AccEventObserversExist()) {
498 return IPC_OK();
501 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
502 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
503 uint32_t type = aIsInsert ? nsIAccessibleEvent::EVENT_TEXT_INSERTED
504 : nsIAccessibleEvent::EVENT_TEXT_REMOVED;
505 nsINode* node = nullptr;
506 RefPtr<xpcAccTextChangeEvent> event = new xpcAccTextChangeEvent(
507 type, xpcAcc, doc, node, aFromUser, aStart, aLen, aIsInsert, aStr);
508 nsCoreUtils::DispatchAccEvent(std::move(event));
510 return IPC_OK();
513 mozilla::ipc::IPCResult DocAccessibleParent::RecvSelectionEvent(
514 const uint64_t& aID, const uint64_t& aWidgetID, const uint32_t& aType) {
515 ACQUIRE_ANDROID_LOCK
516 if (mShutdown) {
517 return IPC_OK();
520 RemoteAccessible* target = GetAccessible(aID);
521 RemoteAccessible* widget = GetAccessible(aWidgetID);
522 if (!target || !widget) {
523 NS_ERROR("invalid id in selection event");
524 return IPC_OK();
527 PlatformSelectionEvent(target, widget, aType);
528 if (!nsCoreUtils::AccEventObserversExist()) {
529 return IPC_OK();
531 xpcAccessibleGeneric* xpcTarget = GetXPCAccessible(target);
532 xpcAccessibleDocument* xpcDoc = GetAccService()->GetXPCDocument(this);
533 RefPtr<xpcAccEvent> event =
534 new xpcAccEvent(aType, xpcTarget, xpcDoc, nullptr, false);
535 nsCoreUtils::DispatchAccEvent(std::move(event));
537 return IPC_OK();
540 mozilla::ipc::IPCResult DocAccessibleParent::RecvVirtualCursorChangeEvent(
541 const uint64_t& aID, const uint64_t& aOldPositionID,
542 const uint64_t& aNewPositionID, const int16_t& aReason,
543 const bool& aFromUser) {
544 ACQUIRE_ANDROID_LOCK
545 if (mShutdown) {
546 return IPC_OK();
549 RemoteAccessible* target = GetAccessible(aID);
550 RemoteAccessible* oldPosition = GetAccessible(aOldPositionID);
551 RemoteAccessible* newPosition = GetAccessible(aNewPositionID);
553 if (!target) {
554 NS_ERROR("no proxy for event!");
555 return IPC_OK();
558 #if defined(ANDROID)
559 PlatformVirtualCursorChangeEvent(target, oldPosition, newPosition, aReason,
560 aFromUser);
561 #endif
563 if (!nsCoreUtils::AccEventObserversExist()) {
564 return IPC_OK();
567 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
568 RefPtr<xpcAccVirtualCursorChangeEvent> event =
569 new xpcAccVirtualCursorChangeEvent(
570 nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED,
571 GetXPCAccessible(target), doc, nullptr, aFromUser,
572 GetXPCAccessible(oldPosition), GetXPCAccessible(newPosition),
573 aReason);
574 nsCoreUtils::DispatchAccEvent(std::move(event));
576 return IPC_OK();
579 mozilla::ipc::IPCResult DocAccessibleParent::RecvScrollingEvent(
580 const uint64_t& aID, const uint64_t& aType, const uint32_t& aScrollX,
581 const uint32_t& aScrollY, const uint32_t& aMaxScrollX,
582 const uint32_t& aMaxScrollY) {
583 ACQUIRE_ANDROID_LOCK
584 if (mShutdown) {
585 return IPC_OK();
588 RemoteAccessible* target = GetAccessible(aID);
589 if (!target) {
590 NS_ERROR("no proxy for event!");
591 return IPC_OK();
594 #if defined(ANDROID)
595 PlatformScrollingEvent(target, aType, aScrollX, aScrollY, aMaxScrollX,
596 aMaxScrollY);
597 #else
598 PlatformEvent(target, aType);
599 #endif
601 if (!nsCoreUtils::AccEventObserversExist()) {
602 return IPC_OK();
605 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
606 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
607 nsINode* node = nullptr;
608 bool fromUser = true; // XXX: Determine if this was from user input.
609 RefPtr<xpcAccScrollingEvent> event =
610 new xpcAccScrollingEvent(aType, xpcAcc, doc, node, fromUser, aScrollX,
611 aScrollY, aMaxScrollX, aMaxScrollY);
612 nsCoreUtils::DispatchAccEvent(std::move(event));
614 return IPC_OK();
617 mozilla::ipc::IPCResult DocAccessibleParent::RecvCache(
618 const mozilla::a11y::CacheUpdateType& aUpdateType,
619 nsTArray<CacheData>&& aData) {
620 ACQUIRE_ANDROID_LOCK
621 if (mShutdown) {
622 return IPC_OK();
625 for (auto& entry : aData) {
626 RemoteAccessible* remote = GetAccessible(entry.ID());
627 if (!remote) {
628 MOZ_ASSERT_UNREACHABLE("No remote found!");
629 continue;
632 remote->ApplyCache(aUpdateType, entry.Fields());
635 if (nsCOMPtr<nsIObserverService> obsService =
636 services::GetObserverService()) {
637 obsService->NotifyObservers(nullptr, NS_ACCESSIBLE_CACHE_TOPIC, nullptr);
640 return IPC_OK();
643 mozilla::ipc::IPCResult DocAccessibleParent::RecvSelectedAccessiblesChanged(
644 nsTArray<uint64_t>&& aSelectedIDs, nsTArray<uint64_t>&& aUnselectedIDs) {
645 ACQUIRE_ANDROID_LOCK
646 if (mShutdown) {
647 return IPC_OK();
650 for (auto& id : aSelectedIDs) {
651 RemoteAccessible* remote = GetAccessible(id);
652 if (!remote) {
653 MOZ_ASSERT_UNREACHABLE("No remote found!");
654 continue;
657 remote->UpdateStateCache(states::SELECTED, true);
660 for (auto& id : aUnselectedIDs) {
661 RemoteAccessible* remote = GetAccessible(id);
662 if (!remote) {
663 MOZ_ASSERT_UNREACHABLE("No remote found!");
664 continue;
667 remote->UpdateStateCache(states::SELECTED, false);
670 if (nsCOMPtr<nsIObserverService> obsService =
671 services::GetObserverService()) {
672 obsService->NotifyObservers(nullptr, NS_ACCESSIBLE_CACHE_TOPIC, nullptr);
675 return IPC_OK();
678 mozilla::ipc::IPCResult DocAccessibleParent::RecvAccessiblesWillMove(
679 nsTArray<uint64_t>&& aIDs) {
680 for (uint64_t id : aIDs) {
681 mMovingIDs.EnsureInserted(id);
683 return IPC_OK();
686 #if !defined(XP_WIN)
687 mozilla::ipc::IPCResult DocAccessibleParent::RecvAnnouncementEvent(
688 const uint64_t& aID, const nsAString& aAnnouncement,
689 const uint16_t& aPriority) {
690 ACQUIRE_ANDROID_LOCK
691 if (mShutdown) {
692 return IPC_OK();
695 RemoteAccessible* target = GetAccessible(aID);
696 if (!target) {
697 NS_ERROR("no proxy for event!");
698 return IPC_OK();
701 # if defined(ANDROID)
702 PlatformAnnouncementEvent(target, aAnnouncement, aPriority);
703 # endif
705 if (!nsCoreUtils::AccEventObserversExist()) {
706 return IPC_OK();
709 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
710 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
711 RefPtr<xpcAccAnnouncementEvent> event = new xpcAccAnnouncementEvent(
712 nsIAccessibleEvent::EVENT_ANNOUNCEMENT, xpcAcc, doc, nullptr, false,
713 aAnnouncement, aPriority);
714 nsCoreUtils::DispatchAccEvent(std::move(event));
716 return IPC_OK();
718 #endif // !defined(XP_WIN)
720 mozilla::ipc::IPCResult DocAccessibleParent::RecvTextSelectionChangeEvent(
721 const uint64_t& aID, nsTArray<TextRangeData>&& aSelection) {
722 ACQUIRE_ANDROID_LOCK
723 if (mShutdown) {
724 return IPC_OK();
727 RemoteAccessible* target = GetAccessible(aID);
728 if (!target) {
729 NS_ERROR("no proxy for event!");
730 return IPC_OK();
733 mTextSelections.ClearAndRetainStorage();
734 mTextSelections.AppendElements(aSelection);
736 #ifdef MOZ_WIDGET_COCOA
737 AutoTArray<TextRange, 1> ranges;
738 SelectionRanges(&ranges);
739 PlatformTextSelectionChangeEvent(target, ranges);
740 #else
741 PlatformEvent(target, nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED);
742 #endif
744 if (!nsCoreUtils::AccEventObserversExist()) {
745 return IPC_OK();
747 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
748 xpcAccessibleDocument* doc = nsAccessibilityService::GetXPCDocument(this);
749 nsINode* node = nullptr;
750 bool fromUser = true; // XXX fix me
751 RefPtr<xpcAccEvent> event =
752 new xpcAccEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED, xpcAcc,
753 doc, node, fromUser);
754 nsCoreUtils::DispatchAccEvent(std::move(event));
756 return IPC_OK();
759 mozilla::ipc::IPCResult DocAccessibleParent::RecvRoleChangedEvent(
760 const a11y::role& aRole, const uint8_t& aRoleMapEntryIndex) {
761 ACQUIRE_ANDROID_LOCK
762 if (mShutdown) {
763 return IPC_OK();
766 mRole = aRole;
767 mRoleMapEntryIndex = aRoleMapEntryIndex;
769 #ifdef MOZ_WIDGET_COCOA
770 PlatformRoleChangedEvent(this, aRole, aRoleMapEntryIndex);
771 #endif
773 return IPC_OK();
776 mozilla::ipc::IPCResult DocAccessibleParent::RecvBindChildDoc(
777 NotNull<PDocAccessibleParent*> aChildDoc, const uint64_t& aID) {
778 ACQUIRE_ANDROID_LOCK
779 // One document should never directly be the child of another.
780 // We should always have at least an outer doc accessible in between.
781 MOZ_ASSERT(aID);
782 if (!aID) return IPC_FAIL(this, "ID is 0!");
784 if (mShutdown) {
785 return IPC_OK();
788 MOZ_ASSERT(CheckDocTree());
790 auto childDoc = static_cast<DocAccessibleParent*>(aChildDoc.get());
791 childDoc->Unbind();
792 ipc::IPCResult result = AddChildDoc(childDoc, aID, false);
793 MOZ_ASSERT(result);
794 MOZ_ASSERT(CheckDocTree());
795 #ifdef DEBUG
796 if (!result) {
797 return result;
799 #else
800 result = IPC_OK();
801 #endif
803 return result;
806 ipc::IPCResult DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc,
807 uint64_t aParentID,
808 bool aCreating) {
809 // We do not use GetAccessible here because we want to be sure to not get the
810 // document it self.
811 ProxyEntry* e = mAccessibles.GetEntry(aParentID);
812 if (!e) {
813 #ifndef FUZZING_SNAPSHOT
814 // This diagnostic assert and the one down below expect a well-behaved
815 // child process. In IPC fuzzing, we directly fuzz parameters of each
816 // method over IPDL and the asserts are not valid under these conditions.
817 MOZ_DIAGNOSTIC_ASSERT(false, "Binding to nonexistent proxy!");
818 #endif
819 return IPC_FAIL(this, "binding to nonexistant proxy!");
822 RemoteAccessible* outerDoc = e->mProxy;
823 MOZ_ASSERT(outerDoc);
825 // OuterDocAccessibles are expected to only have a document as a child.
826 // However for compatibility we tolerate replacing one document with another
827 // here.
828 if (!outerDoc->IsOuterDoc() || outerDoc->ChildCount() > 1 ||
829 (outerDoc->ChildCount() == 1 && !outerDoc->RemoteChildAt(0)->IsDoc())) {
830 #ifndef FUZZING_SNAPSHOT
831 MOZ_DIAGNOSTIC_ASSERT(false,
832 "Binding to parent that isn't a valid OuterDoc!");
833 #endif
834 return IPC_FAIL(this, "Binding to parent that isn't a valid OuterDoc!");
837 if (outerDoc->ChildCount() == 1) {
838 MOZ_ASSERT(outerDoc->RemoteChildAt(0)->AsDoc());
839 outerDoc->RemoteChildAt(0)->AsDoc()->Unbind();
842 aChildDoc->SetParent(outerDoc);
843 outerDoc->SetChildDoc(aChildDoc);
844 mChildDocs.AppendElement(aChildDoc->mActorID);
845 aChildDoc->mParentDoc = mActorID;
847 if (aCreating) {
848 ProxyCreated(aChildDoc);
851 if (aChildDoc->IsTopLevelInContentProcess()) {
852 // aChildDoc is an embedded document in a different content process to
853 // this document.
854 auto embeddedBrowser =
855 static_cast<dom::BrowserParent*>(aChildDoc->Manager());
856 dom::BrowserBridgeParent* bridge =
857 embeddedBrowser->GetBrowserBridgeParent();
858 if (bridge) {
859 #if defined(XP_WIN)
860 if (nsWinUtils::IsWindowEmulationStarted()) {
861 aChildDoc->SetEmulatedWindowHandle(mEmulatedWindowHandle);
863 #endif // defined(XP_WIN)
864 // We need to fire a reorder event on the outer doc accessible.
865 // For same-process documents, this is fired by the content process, but
866 // this isn't possible when the document is in a different process to its
867 // embedder.
868 // FireEvent fires both OS and XPCOM events.
869 FireEvent(outerDoc, nsIAccessibleEvent::EVENT_REORDER);
873 return IPC_OK();
876 ipc::IPCResult DocAccessibleParent::AddChildDoc(
877 dom::BrowserBridgeParent* aBridge) {
878 MOZ_ASSERT(aBridge->GetEmbedderAccessibleDoc() == this);
879 uint64_t parentId = aBridge->GetEmbedderAccessibleId();
880 MOZ_ASSERT(parentId);
881 if (!mAccessibles.GetEntry(parentId)) {
882 // Sometimes, this gets called before the embedder sends us the
883 // OuterDocAccessible. We must add the child when the OuterDocAccessible
884 // gets created later.
885 mPendingOOPChildDocs.Insert(aBridge);
886 return IPC_OK();
888 return AddChildDoc(aBridge->GetDocAccessibleParent(), parentId,
889 /* aCreating */ false);
892 mozilla::ipc::IPCResult DocAccessibleParent::RecvShutdown() {
893 ACQUIRE_ANDROID_LOCK
894 Destroy();
896 auto mgr = static_cast<dom::BrowserParent*>(Manager());
897 if (!mgr->IsDestroyed()) {
898 if (!PDocAccessibleParent::Send__delete__(this)) {
899 return IPC_FAIL_NO_REASON(mgr);
903 return IPC_OK();
906 void DocAccessibleParent::Destroy() {
907 // If we are already shutdown that is because our containing tab parent is
908 // shutting down in which case we don't need to do anything.
909 if (mShutdown) {
910 return;
913 mShutdown = true;
914 mBrowsingContext = nullptr;
916 #ifdef ANDROID
917 if (FocusMgr() && FocusMgr()->IsFocusedRemoteDoc(this)) {
918 FocusMgr()->SetFocusedRemoteDoc(nullptr);
920 #endif
922 MOZ_DIAGNOSTIC_ASSERT(LiveDocs().Contains(mActorID));
923 uint32_t childDocCount = mChildDocs.Length();
924 for (uint32_t i = 0; i < childDocCount; i++) {
925 for (uint32_t j = i + 1; j < childDocCount; j++) {
926 MOZ_DIAGNOSTIC_ASSERT(mChildDocs[i] != mChildDocs[j]);
930 // XXX This indirection through the hash map of live documents shouldn't be
931 // needed, but be paranoid for now.
932 int32_t actorID = mActorID;
933 for (uint32_t i = childDocCount - 1; i < childDocCount; i--) {
934 DocAccessibleParent* thisDoc = LiveDocs().Get(actorID);
935 MOZ_ASSERT(thisDoc);
936 if (!thisDoc) {
937 return;
940 thisDoc->ChildDocAt(i)->Destroy();
943 for (auto iter = mAccessibles.Iter(); !iter.Done(); iter.Next()) {
944 RemoteAccessible* acc = iter.Get()->mProxy;
945 MOZ_ASSERT(acc != this);
946 if (acc->IsTable()) {
947 CachedTableAccessible::Invalidate(acc);
949 ProxyDestroyed(acc);
950 iter.Remove();
953 DocAccessibleParent* thisDoc = LiveDocs().Get(actorID);
954 MOZ_ASSERT(thisDoc);
955 if (!thisDoc) {
956 return;
959 mChildren.Clear();
960 // The code above should have already completely cleared these, but to be
961 // extra safe make sure they are cleared here.
962 thisDoc->mAccessibles.Clear();
963 thisDoc->mChildDocs.Clear();
965 DocManager::NotifyOfRemoteDocShutdown(thisDoc);
966 thisDoc = LiveDocs().Get(actorID);
967 MOZ_ASSERT(thisDoc);
968 if (!thisDoc) {
969 return;
972 ProxyDestroyed(thisDoc);
973 thisDoc = LiveDocs().Get(actorID);
974 MOZ_ASSERT(thisDoc);
975 if (!thisDoc) {
976 return;
979 if (DocAccessibleParent* parentDoc = thisDoc->ParentDoc()) {
980 parentDoc->RemoveChildDoc(thisDoc);
981 } else if (IsTopLevel()) {
982 GetAccService()->RemoteDocShutdown(this);
986 void DocAccessibleParent::ActorDestroy(ActorDestroyReason aWhy) {
987 MOZ_ASSERT(CheckDocTree());
988 if (!mShutdown) {
989 ACQUIRE_ANDROID_LOCK
990 Destroy();
994 DocAccessibleParent* DocAccessibleParent::ParentDoc() const {
995 if (mParentDoc == kNoParentDoc) {
996 return nullptr;
999 return LiveDocs().Get(mParentDoc);
1002 bool DocAccessibleParent::CheckDocTree() const {
1003 size_t childDocs = mChildDocs.Length();
1004 for (size_t i = 0; i < childDocs; i++) {
1005 const DocAccessibleParent* childDoc = ChildDocAt(i);
1006 if (!childDoc || childDoc->ParentDoc() != this) return false;
1008 if (!childDoc->CheckDocTree()) {
1009 return false;
1013 return true;
1016 xpcAccessibleGeneric* DocAccessibleParent::GetXPCAccessible(
1017 RemoteAccessible* aProxy) {
1018 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
1019 MOZ_ASSERT(doc);
1021 return doc->GetAccessible(aProxy);
1024 #if defined(XP_WIN)
1025 void DocAccessibleParent::MaybeInitWindowEmulation() {
1026 if (!nsWinUtils::IsWindowEmulationStarted()) {
1027 return;
1030 // XXX get the bounds from the browserParent instead of poking at accessibles
1031 // which might not exist yet.
1032 LocalAccessible* outerDoc = OuterDocOfRemoteBrowser();
1033 if (!outerDoc) {
1034 return;
1037 RootAccessible* rootDocument = outerDoc->RootAccessible();
1038 MOZ_ASSERT(rootDocument);
1040 bool isActive = true;
1041 LayoutDeviceIntRect rect(CW_USEDEFAULT, CW_USEDEFAULT, 0, 0);
1042 if (Compatibility::IsDolphin()) {
1043 rect = Bounds();
1044 LayoutDeviceIntRect rootRect = rootDocument->Bounds();
1045 rect.MoveToX(rootRect.X() - rect.X());
1046 rect.MoveToY(rect.Y() - rootRect.Y());
1048 auto browserParent = static_cast<dom::BrowserParent*>(Manager());
1049 isActive = browserParent->GetDocShellIsActive();
1052 // onCreate is guaranteed to be called synchronously by
1053 // nsWinUtils::CreateNativeWindow, so this reference isn't really necessary.
1054 // However, static analysis complains without it.
1055 RefPtr<DocAccessibleParent> thisRef = this;
1056 nsWinUtils::NativeWindowCreateProc onCreate([thisRef](HWND aHwnd) -> void {
1057 ::SetPropW(aHwnd, kPropNameDocAccParent,
1058 reinterpret_cast<HANDLE>(thisRef.get()));
1059 thisRef->SetEmulatedWindowHandle(aHwnd);
1062 HWND parentWnd = reinterpret_cast<HWND>(rootDocument->GetNativeWindow());
1063 DebugOnly<HWND> hWnd = nsWinUtils::CreateNativeWindow(
1064 kClassNameTabContent, parentWnd, rect.X(), rect.Y(), rect.Width(),
1065 rect.Height(), isActive, &onCreate);
1066 MOZ_ASSERT(hWnd);
1069 void DocAccessibleParent::SetEmulatedWindowHandle(HWND aWindowHandle) {
1070 if (!aWindowHandle && mEmulatedWindowHandle && IsTopLevel()) {
1071 ::DestroyWindow(mEmulatedWindowHandle);
1073 mEmulatedWindowHandle = aWindowHandle;
1075 #endif // defined(XP_WIN)
1077 mozilla::ipc::IPCResult DocAccessibleParent::RecvFocusEvent(
1078 const uint64_t& aID, const LayoutDeviceIntRect& aCaretRect) {
1079 ACQUIRE_ANDROID_LOCK
1080 if (mShutdown) {
1081 return IPC_OK();
1084 RemoteAccessible* proxy = GetAccessible(aID);
1085 if (!proxy) {
1086 NS_ERROR("no proxy for event!");
1087 return IPC_OK();
1090 #ifdef ANDROID
1091 if (FocusMgr()) {
1092 FocusMgr()->SetFocusedRemoteDoc(this);
1094 #endif
1096 mFocus = aID;
1097 PlatformFocusEvent(proxy, aCaretRect);
1099 if (!nsCoreUtils::AccEventObserversExist()) {
1100 return IPC_OK();
1103 xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
1104 xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
1105 nsINode* node = nullptr;
1106 bool fromUser = true; // XXX fix me
1107 RefPtr<xpcAccEvent> event = new xpcAccEvent(nsIAccessibleEvent::EVENT_FOCUS,
1108 xpcAcc, doc, node, fromUser);
1109 nsCoreUtils::DispatchAccEvent(std::move(event));
1111 return IPC_OK();
1114 void DocAccessibleParent::SelectionRanges(nsTArray<TextRange>* aRanges) const {
1115 aRanges->SetCapacity(mTextSelections.Length());
1116 for (const auto& data : mTextSelections) {
1117 // Selection ranges should usually be in sync with the tree. However, tree
1118 // and selection updates happen using separate IPDL calls, so it's possible
1119 // for a client selection query to arrive between them. Thus, we validate
1120 // the Accessibles and offsets here.
1121 auto* startAcc =
1122 const_cast<RemoteAccessible*>(GetAccessible(data.StartID()));
1123 auto* endAcc = const_cast<RemoteAccessible*>(GetAccessible(data.EndID()));
1124 if (!startAcc || !endAcc) {
1125 continue;
1127 uint32_t startCount = startAcc->CharacterCount();
1128 if (startCount == 0 ||
1129 data.StartOffset() > static_cast<int32_t>(startCount)) {
1130 continue;
1132 uint32_t endCount = endAcc->CharacterCount();
1133 if (endCount == 0 || data.EndOffset() > static_cast<int32_t>(endCount)) {
1134 continue;
1136 aRanges->AppendElement(TextRange(const_cast<DocAccessibleParent*>(this),
1137 startAcc, data.StartOffset(), endAcc,
1138 data.EndOffset()));
1142 Accessible* DocAccessibleParent::FocusedChild() {
1143 LocalAccessible* outerDoc = OuterDocOfRemoteBrowser();
1144 if (!outerDoc) {
1145 return nullptr;
1148 RootAccessible* rootDocument = outerDoc->RootAccessible();
1149 return rootDocument->FocusedChild();
1152 void DocAccessibleParent::URL(nsACString& aURL) const {
1153 if (!mBrowsingContext) {
1154 return;
1156 nsCOMPtr<nsIURI> uri = mBrowsingContext->GetCurrentURI();
1157 if (!uri) {
1158 return;
1160 // Let's avoid treating too long URI in the main process for avoiding
1161 // memory fragmentation as far as possible.
1162 if (uri->SchemeIs("data") || uri->SchemeIs("blob")) {
1163 return;
1165 nsCOMPtr<nsIIOService> io = mozilla::components::IO::Service();
1166 if (NS_WARN_IF(!io)) {
1167 return;
1169 nsCOMPtr<nsIURI> exposableURI;
1170 if (NS_FAILED(io->CreateExposableURI(uri, getter_AddRefs(exposableURI))) ||
1171 MOZ_UNLIKELY(!exposableURI)) {
1172 return;
1174 exposableURI->GetSpec(aURL);
1177 void DocAccessibleParent::URL(nsAString& aURL) const {
1178 nsAutoCString url;
1179 URL(url);
1180 CopyUTF8toUTF16(url, aURL);
1183 void DocAccessibleParent::MimeType(nsAString& aMime) const {
1184 if (mCachedFields) {
1185 mCachedFields->GetAttribute(CacheKey::MimeType, aMime);
1189 Relation DocAccessibleParent::RelationByType(RelationType aType) const {
1190 // If the accessible is top-level, provide the NODE_CHILD_OF relation so that
1191 // MSAA clients can easily get to true parent instead of getting to oleacc's
1192 // ROLE_WINDOW accessible when window emulation is enabled which will prevent
1193 // us from going up further (because it is system generated and has no idea
1194 // about the hierarchy above it).
1195 if (aType == RelationType::NODE_CHILD_OF && IsTopLevel()) {
1196 return Relation(Parent());
1199 return RemoteAccessible::RelationByType(aType);
1202 DocAccessibleParent* DocAccessibleParent::GetFrom(
1203 dom::BrowsingContext* aBrowsingContext) {
1204 if (!aBrowsingContext) {
1205 return nullptr;
1208 dom::BrowserParent* bp = aBrowsingContext->Canonical()->GetBrowserParent();
1209 if (!bp) {
1210 return nullptr;
1213 const ManagedContainer<PDocAccessibleParent>& docs =
1214 bp->ManagedPDocAccessibleParent();
1215 for (auto* key : docs) {
1216 // Iterate over our docs until we find one with a browsing
1217 // context that matches the one we passed in. Return that
1218 // document.
1219 auto* doc = static_cast<a11y::DocAccessibleParent*>(key);
1220 if (doc->GetBrowsingContext() == aBrowsingContext) {
1221 return doc;
1225 return nullptr;
1228 size_t DocAccessibleParent::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) {
1229 size_t size = 0;
1231 size += RemoteAccessible::SizeOfExcludingThis(aMallocSizeOf);
1233 size += mReverseRelations.ShallowSizeOfExcludingThis(aMallocSizeOf);
1234 for (auto i = mReverseRelations.Iter(); !i.Done(); i.Next()) {
1235 size += i.Data().ShallowSizeOfExcludingThis(aMallocSizeOf);
1236 for (auto j = i.Data().Iter(); !j.Done(); j.Next()) {
1237 size += j.Data().ShallowSizeOfExcludingThis(aMallocSizeOf);
1241 size += mOnScreenAccessibles.ShallowSizeOfExcludingThis(aMallocSizeOf);
1243 size += mChildDocs.ShallowSizeOfExcludingThis(aMallocSizeOf);
1245 size += mAccessibles.ShallowSizeOfExcludingThis(aMallocSizeOf);
1246 for (auto i = mAccessibles.Iter(); !i.Done(); i.Next()) {
1247 size += i.Get()->mProxy->SizeOfIncludingThis(aMallocSizeOf);
1250 size += mPendingOOPChildDocs.ShallowSizeOfExcludingThis(aMallocSizeOf);
1252 // The mTextSelections array contains structs of integers. We can count them
1253 // by counting the size of the array - there's no deep structure here.
1254 size += mTextSelections.ShallowSizeOfExcludingThis(aMallocSizeOf);
1256 return size;
1259 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOfAccessibilityCache);
1261 NS_IMETHODIMP
1262 DocAccessibleParent::CollectReports(nsIHandleReportCallback* aHandleReport,
1263 nsISupports* aData, bool aAnon) {
1264 nsAutoCString path;
1266 if (aAnon) {
1267 path = nsPrintfCString("explicit/a11y/cache(%" PRIu64 ")", mActorID);
1268 } else {
1269 nsCString url;
1270 URL(url);
1271 url.ReplaceChar(
1272 '/', '\\'); // Tell the memory reporter this is not a path seperator.
1273 path = nsPrintfCString("explicit/a11y/cache(%s)", url.get());
1276 aHandleReport->Callback(
1277 /* process */ ""_ns, path, KIND_HEAP, UNITS_BYTES,
1278 SizeOfIncludingThis(MallocSizeOfAccessibilityCache),
1279 nsLiteralCString("Size of the accessability cache for this document."),
1280 aData);
1282 return NS_OK;
1285 NS_IMPL_ISUPPORTS(DocAccessibleParent, nsIMemoryReporter);
1287 } // namespace a11y
1288 } // namespace mozilla