Bug 1800790 [wpt PR 36982] - Add makotokato as reviewer, a=testonly
[gecko.git] / accessible / ipc / DocAccessibleChildBase.cpp
blob538489500844e2865202a165c96bbcf67bc4c875
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 "mozilla/a11y/DocAccessibleChildBase.h"
8 #include "mozilla/a11y/CacheConstants.h"
9 #include "mozilla/a11y/RemoteAccessible.h"
10 #include "mozilla/ipc/ProcessChild.h"
11 #include "mozilla/StaticPrefs_accessibility.h"
13 #include "LocalAccessible-inl.h"
14 #ifdef A11Y_LOG
15 # include "Logging.h"
16 #endif
18 namespace mozilla {
19 namespace a11y {
21 /* static */
22 void DocAccessibleChildBase::FlattenTree(LocalAccessible* aRoot,
23 nsTArray<LocalAccessible*>& aTree) {
24 MOZ_ASSERT(!aRoot->IsDoc(), "documents shouldn't be serialized");
26 aTree.AppendElement(aRoot);
27 // OuterDocAccessibles are special because we don't want to serialize the
28 // child doc here, we'll call PDocAccessibleConstructor in
29 // NotificationController.
30 uint32_t childCount = aRoot->IsOuterDoc() ? 0 : aRoot->ChildCount();
32 for (uint32_t i = 0; i < childCount; i++) {
33 FlattenTree(aRoot->LocalChildAt(i), aTree);
37 /* static */
38 void DocAccessibleChildBase::SerializeTree(nsTArray<LocalAccessible*>& aTree,
39 nsTArray<AccessibleData>& aData) {
40 for (LocalAccessible* acc : aTree) {
41 uint64_t id = reinterpret_cast<uint64_t>(acc->UniqueID());
42 #if defined(XP_WIN)
43 int32_t msaaId = StaticPrefs::accessibility_cache_enabled_AtStartup()
44 ? 0
45 : MsaaAccessible::GetChildIDFor(acc);
46 #endif
47 a11y::role role = acc->Role();
48 uint32_t childCount = acc->IsOuterDoc() ? 0 : acc->ChildCount();
50 uint32_t genericTypes = acc->mGenericTypes;
51 if (acc->ARIAHasNumericValue()) {
52 // XXX: We need to do this because this requires a state check.
53 genericTypes |= eNumericValue;
55 if (acc->IsTextLeaf() || acc->IsImage()) {
56 // Ideally, we'd set eActionable for any Accessible with an ancedstor
57 // action. However, that requires an ancestor walk which is too expensive
58 // here. eActionable is only used by ATK. For now, we only expose ancestor
59 // actions on text leaf and image Accessibles. This means that we don't
60 // support "click ancestor" for ATK.
61 if (acc->ActionCount()) {
62 genericTypes |= eActionable;
64 } else if (acc->HasPrimaryAction()) {
65 genericTypes |= eActionable;
68 #if defined(XP_WIN)
69 aData.AppendElement(AccessibleData(
70 id, msaaId, role, childCount, static_cast<AccType>(acc->mType),
71 static_cast<AccGenericType>(genericTypes), acc->mRoleMapEntryIndex));
72 #else
73 aData.AppendElement(AccessibleData(
74 id, role, childCount, static_cast<AccType>(acc->mType),
75 static_cast<AccGenericType>(genericTypes), acc->mRoleMapEntryIndex));
76 #endif
80 void DocAccessibleChildBase::InsertIntoIpcTree(LocalAccessible* aParent,
81 LocalAccessible* aChild,
82 uint32_t aIdxInParent,
83 bool aSuppressShowEvent) {
84 uint64_t parentID =
85 aParent->IsDoc() ? 0 : reinterpret_cast<uint64_t>(aParent->UniqueID());
86 nsTArray<LocalAccessible*> shownTree;
87 FlattenTree(aChild, shownTree);
88 ShowEventData data(parentID, aIdxInParent,
89 nsTArray<AccessibleData>(shownTree.Length()),
90 aSuppressShowEvent ||
91 StaticPrefs::accessibility_cache_enabled_AtStartup());
92 SerializeTree(shownTree, data.NewTree());
93 if (ipc::ProcessChild::ExpectingShutdown()) {
94 return;
96 MaybeSendShowEvent(data, false);
97 if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
98 nsTArray<CacheData> cache(shownTree.Length());
99 for (LocalAccessible* acc : shownTree) {
100 if (mDoc->IsAccessibleBeingMoved(acc)) {
101 // Even though we send moves as a hide and a show, we don't want to
102 // push the cache again for moves.
103 continue;
105 RefPtr<AccAttributes> fields =
106 acc->BundleFieldsForCache(CacheDomain::All, CacheUpdateType::Initial);
107 if (fields->Count()) {
108 uint64_t id = reinterpret_cast<uint64_t>(acc->UniqueID());
109 cache.AppendElement(CacheData(id, fields));
112 Unused << SendCache(CacheUpdateType::Initial, cache, true);
116 void DocAccessibleChildBase::ShowEvent(AccShowEvent* aShowEvent) {
117 LocalAccessible* child = aShowEvent->GetAccessible();
118 InsertIntoIpcTree(aShowEvent->LocalParent(), child, child->IndexInParent(),
119 false);
122 mozilla::ipc::IPCResult DocAccessibleChildBase::RecvTakeFocus(
123 const uint64_t& aID) {
124 LocalAccessible* acc = IdToAccessible(aID);
125 if (acc) {
126 acc->TakeFocus();
128 return IPC_OK();
131 mozilla::ipc::IPCResult DocAccessibleChildBase::RecvScrollTo(
132 const uint64_t& aID, const uint32_t& aScrollType) {
133 LocalAccessible* acc = IdToAccessible(aID);
134 if (acc) {
135 RefPtr<PresShell> presShell = acc->Document()->PresShellPtr();
136 nsCOMPtr<nsIContent> content = acc->GetContent();
137 nsCoreUtils::ScrollTo(presShell, content, aScrollType);
140 return IPC_OK();
143 mozilla::ipc::IPCResult DocAccessibleChildBase::RecvTakeSelection(
144 const uint64_t& aID) {
145 LocalAccessible* acc = IdToAccessible(aID);
146 if (acc) {
147 acc->TakeSelection();
150 return IPC_OK();
153 mozilla::ipc::IPCResult DocAccessibleChildBase::RecvSetSelected(
154 const uint64_t& aID, const bool& aSelect) {
155 LocalAccessible* acc = IdToAccessible(aID);
156 if (acc) {
157 acc->SetSelected(aSelect);
160 return IPC_OK();
163 mozilla::ipc::IPCResult DocAccessibleChildBase::RecvVerifyCache(
164 const uint64_t& aID, const uint64_t& aCacheDomain, AccAttributes* aFields) {
165 #ifdef A11Y_LOG
166 LocalAccessible* acc = IdToAccessible(aID);
167 if (!acc) {
168 return IPC_OK();
171 RefPtr<AccAttributes> localFields =
172 acc->BundleFieldsForCache(aCacheDomain, CacheUpdateType::Update);
173 bool mismatches = false;
175 for (auto prop : *localFields) {
176 if (prop.Value<DeleteEntry>()) {
177 if (aFields->HasAttribute(prop.Name())) {
178 if (!mismatches) {
179 logging::MsgBegin("Mismatch!", "Local and remote values differ");
180 logging::AccessibleInfo("", acc);
181 mismatches = true;
183 nsAutoCString propName;
184 prop.Name()->ToUTF8String(propName);
185 nsAutoString val;
186 aFields->GetAttribute(prop.Name(), val);
187 logging::MsgEntry(
188 "Remote value for %s should be empty, but instead it is '%s'",
189 propName.get(), NS_ConvertUTF16toUTF8(val).get());
191 continue;
194 nsAutoString localVal;
195 prop.ValueAsString(localVal);
196 nsAutoString remoteVal;
197 aFields->GetAttribute(prop.Name(), remoteVal);
198 if (!localVal.Equals(remoteVal)) {
199 if (!mismatches) {
200 logging::MsgBegin("Mismatch!", "Local and remote values differ");
201 logging::AccessibleInfo("", acc);
202 mismatches = true;
204 nsAutoCString propName;
205 prop.Name()->ToUTF8String(propName);
206 logging::MsgEntry("Fields differ: %s '%s' != '%s'", propName.get(),
207 NS_ConvertUTF16toUTF8(remoteVal).get(),
208 NS_ConvertUTF16toUTF8(localVal).get());
211 if (mismatches) {
212 logging::MsgEnd();
214 #endif // A11Y_LOG
216 return IPC_OK();
219 mozilla::ipc::IPCResult DocAccessibleChildBase::RecvDoActionAsync(
220 const uint64_t& aID, const uint8_t& aIndex) {
221 if (LocalAccessible* acc = IdToAccessible(aID)) {
222 Unused << acc->DoAction(aIndex);
225 return IPC_OK();
228 mozilla::ipc::IPCResult DocAccessibleChildBase::RecvSetCaretOffset(
229 const uint64_t& aID, const int32_t& aOffset) {
230 HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
231 if (acc && acc->IsTextRole() && acc->IsValidOffset(aOffset)) {
232 acc->SetCaretOffset(aOffset);
234 return IPC_OK();
237 LocalAccessible* DocAccessibleChildBase::IdToAccessible(
238 const uint64_t& aID) const {
239 if (!aID) return mDoc;
241 if (!mDoc) return nullptr;
243 return mDoc->GetAccessibleByUniqueID(reinterpret_cast<void*>(aID));
246 HyperTextAccessible* DocAccessibleChildBase::IdToHyperTextAccessible(
247 const uint64_t& aID) const {
248 LocalAccessible* acc = IdToAccessible(aID);
249 return acc && acc->IsHyperText() ? acc->AsHyperText() : nullptr;
252 } // namespace a11y
253 } // namespace mozilla