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"
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
);
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());
43 int32_t msaaId
= StaticPrefs::accessibility_cache_enabled_AtStartup()
45 : MsaaAccessible::GetChildIDFor(acc
);
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
;
69 aData
.AppendElement(AccessibleData(
70 id
, msaaId
, role
, childCount
, static_cast<AccType
>(acc
->mType
),
71 static_cast<AccGenericType
>(genericTypes
), acc
->mRoleMapEntryIndex
));
73 aData
.AppendElement(AccessibleData(
74 id
, role
, childCount
, static_cast<AccType
>(acc
->mType
),
75 static_cast<AccGenericType
>(genericTypes
), acc
->mRoleMapEntryIndex
));
80 void DocAccessibleChildBase::InsertIntoIpcTree(LocalAccessible
* aParent
,
81 LocalAccessible
* aChild
,
82 uint32_t aIdxInParent
,
83 bool aSuppressShowEvent
) {
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()),
91 StaticPrefs::accessibility_cache_enabled_AtStartup());
92 SerializeTree(shownTree
, data
.NewTree());
93 if (ipc::ProcessChild::ExpectingShutdown()) {
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.
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(),
122 mozilla::ipc::IPCResult
DocAccessibleChildBase::RecvTakeFocus(
123 const uint64_t& aID
) {
124 LocalAccessible
* acc
= IdToAccessible(aID
);
131 mozilla::ipc::IPCResult
DocAccessibleChildBase::RecvScrollTo(
132 const uint64_t& aID
, const uint32_t& aScrollType
) {
133 LocalAccessible
* acc
= IdToAccessible(aID
);
135 RefPtr
<PresShell
> presShell
= acc
->Document()->PresShellPtr();
136 nsCOMPtr
<nsIContent
> content
= acc
->GetContent();
137 nsCoreUtils::ScrollTo(presShell
, content
, aScrollType
);
143 mozilla::ipc::IPCResult
DocAccessibleChildBase::RecvTakeSelection(
144 const uint64_t& aID
) {
145 LocalAccessible
* acc
= IdToAccessible(aID
);
147 acc
->TakeSelection();
153 mozilla::ipc::IPCResult
DocAccessibleChildBase::RecvSetSelected(
154 const uint64_t& aID
, const bool& aSelect
) {
155 LocalAccessible
* acc
= IdToAccessible(aID
);
157 acc
->SetSelected(aSelect
);
163 mozilla::ipc::IPCResult
DocAccessibleChildBase::RecvVerifyCache(
164 const uint64_t& aID
, const uint64_t& aCacheDomain
, AccAttributes
* aFields
) {
166 LocalAccessible
* acc
= IdToAccessible(aID
);
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())) {
179 logging::MsgBegin("Mismatch!", "Local and remote values differ");
180 logging::AccessibleInfo("", acc
);
183 nsAutoCString propName
;
184 prop
.Name()->ToUTF8String(propName
);
186 aFields
->GetAttribute(prop
.Name(), val
);
188 "Remote value for %s should be empty, but instead it is '%s'",
189 propName
.get(), NS_ConvertUTF16toUTF8(val
).get());
194 nsAutoString localVal
;
195 prop
.ValueAsString(localVal
);
196 nsAutoString remoteVal
;
197 aFields
->GetAttribute(prop
.Name(), remoteVal
);
198 if (!localVal
.Equals(remoteVal
)) {
200 logging::MsgBegin("Mismatch!", "Local and remote values differ");
201 logging::AccessibleInfo("", acc
);
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());
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
);
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
);
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;
253 } // namespace mozilla