Bug 1856976 [wpt PR 42335] - Update mutation event suppression for <details name...
[gecko.git] / accessible / ipc / DocAccessibleChild.cpp
blob67f0dd0b3e88b0c751eb2da26da91f1b7fd50850
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 "chrome/common/ipc_channel.h"
8 #include "mozilla/a11y/DocAccessibleChild.h"
9 #include "mozilla/a11y/CacheConstants.h"
10 #include "mozilla/a11y/FocusManager.h"
11 #include "mozilla/ipc/ProcessChild.h"
12 #include "nsAccessibilityService.h"
14 #include "LocalAccessible-inl.h"
15 #ifdef A11Y_LOG
16 # include "Logging.h"
17 #endif
18 #include "TextLeafRange.h"
20 namespace mozilla {
21 namespace a11y {
23 /* static */
24 void DocAccessibleChild::FlattenTree(LocalAccessible* aRoot,
25 nsTArray<LocalAccessible*>& aTree) {
26 MOZ_ASSERT(!aRoot->IsDoc(), "documents shouldn't be serialized");
28 aTree.AppendElement(aRoot);
29 // OuterDocAccessibles are special because we don't want to serialize the
30 // child doc here, we'll call PDocAccessibleConstructor in
31 // NotificationController.
32 uint32_t childCount = aRoot->IsOuterDoc() ? 0 : aRoot->ChildCount();
34 for (uint32_t i = 0; i < childCount; i++) {
35 FlattenTree(aRoot->LocalChildAt(i), aTree);
39 /* static */
40 AccessibleData DocAccessibleChild::SerializeAcc(LocalAccessible* aAcc) {
41 uint32_t genericTypes = aAcc->mGenericTypes;
42 if (aAcc->ARIAHasNumericValue()) {
43 // XXX: We need to do this because this requires a state check.
44 genericTypes |= eNumericValue;
46 if (aAcc->IsTextLeaf() || aAcc->IsImage()) {
47 // Ideally, we'd set eActionable for any Accessible with an ancedstor
48 // action. However, that requires an ancestor walk which is too expensive
49 // here. eActionable is only used by ATK. For now, we only expose ancestor
50 // actions on text leaf and image Accessibles. This means that we don't
51 // support "click ancestor" for ATK.
52 if (aAcc->ActionCount()) {
53 genericTypes |= eActionable;
55 } else if (aAcc->HasPrimaryAction()) {
56 genericTypes |= eActionable;
59 RefPtr<AccAttributes> fields;
60 // Even though we send moves as a hide and a show, we don't want to
61 // push the cache again for moves.
62 if (!aAcc->Document()->IsAccessibleBeingMoved(aAcc)) {
63 fields =
64 aAcc->BundleFieldsForCache(CacheDomain::All, CacheUpdateType::Initial);
65 if (fields->Count() == 0) {
66 fields = nullptr;
70 return AccessibleData(aAcc->ID(), aAcc->Role(), aAcc->LocalParent()->ID(),
71 static_cast<int32_t>(aAcc->IndexInParent()),
72 static_cast<AccType>(aAcc->mType),
73 static_cast<AccGenericType>(genericTypes),
74 aAcc->mRoleMapEntryIndex, fields);
77 void DocAccessibleChild::InsertIntoIpcTree(LocalAccessible* aChild,
78 bool aSuppressShowEvent) {
79 nsTArray<LocalAccessible*> shownTree;
80 FlattenTree(aChild, shownTree);
81 uint32_t totalAccs = shownTree.Length();
82 // Exceeding the IPDL maximum message size will cause a crash. Try to avoid
83 // this by only including kMaxAccsPerMessage Accessibels in a single IPDL
84 // call. If there are Accessibles beyond this, they will be split across
85 // multiple calls.
86 constexpr uint32_t kMaxAccsPerMessage =
87 IPC::Channel::kMaximumMessageSize / (2 * 1024);
88 nsTArray<AccessibleData> data(std::min(kMaxAccsPerMessage, totalAccs));
89 for (LocalAccessible* child : shownTree) {
90 if (data.Length() == kMaxAccsPerMessage) {
91 if (ipc::ProcessChild::ExpectingShutdown()) {
92 return;
94 SendShowEvent(data, aSuppressShowEvent, false, false);
95 data.ClearAndRetainStorage();
97 data.AppendElement(SerializeAcc(child));
99 if (ipc::ProcessChild::ExpectingShutdown()) {
100 return;
102 if (!data.IsEmpty()) {
103 SendShowEvent(data, aSuppressShowEvent, true, false);
107 void DocAccessibleChild::ShowEvent(AccShowEvent* aShowEvent) {
108 LocalAccessible* child = aShowEvent->GetAccessible();
109 InsertIntoIpcTree(child, false);
112 mozilla::ipc::IPCResult DocAccessibleChild::RecvTakeFocus(const uint64_t& aID) {
113 LocalAccessible* acc = IdToAccessible(aID);
114 if (acc) {
115 acc->TakeFocus();
117 return IPC_OK();
120 mozilla::ipc::IPCResult DocAccessibleChild::RecvScrollTo(
121 const uint64_t& aID, const uint32_t& aScrollType) {
122 LocalAccessible* acc = IdToAccessible(aID);
123 if (acc) {
124 RefPtr<PresShell> presShell = acc->Document()->PresShellPtr();
125 nsCOMPtr<nsIContent> content = acc->GetContent();
126 nsCoreUtils::ScrollTo(presShell, content, aScrollType);
129 return IPC_OK();
132 mozilla::ipc::IPCResult DocAccessibleChild::RecvTakeSelection(
133 const uint64_t& aID) {
134 LocalAccessible* acc = IdToAccessible(aID);
135 if (acc) {
136 acc->TakeSelection();
139 return IPC_OK();
142 mozilla::ipc::IPCResult DocAccessibleChild::RecvSetSelected(
143 const uint64_t& aID, const bool& aSelect) {
144 LocalAccessible* acc = IdToAccessible(aID);
145 if (acc) {
146 acc->SetSelected(aSelect);
149 return IPC_OK();
152 mozilla::ipc::IPCResult DocAccessibleChild::RecvVerifyCache(
153 const uint64_t& aID, const uint64_t& aCacheDomain, AccAttributes* aFields) {
154 #ifdef A11Y_LOG
155 LocalAccessible* acc = IdToAccessible(aID);
156 if (!acc) {
157 return IPC_OK();
160 RefPtr<AccAttributes> localFields =
161 acc->BundleFieldsForCache(aCacheDomain, CacheUpdateType::Update);
162 bool mismatches = false;
164 for (auto prop : *localFields) {
165 if (prop.Value<DeleteEntry>()) {
166 if (aFields->HasAttribute(prop.Name())) {
167 if (!mismatches) {
168 logging::MsgBegin("Mismatch!", "Local and remote values differ");
169 logging::AccessibleInfo("", acc);
170 mismatches = true;
172 nsAutoCString propName;
173 prop.Name()->ToUTF8String(propName);
174 nsAutoString val;
175 aFields->GetAttribute(prop.Name(), val);
176 logging::MsgEntry(
177 "Remote value for %s should be empty, but instead it is '%s'",
178 propName.get(), NS_ConvertUTF16toUTF8(val).get());
180 continue;
183 nsAutoString localVal;
184 prop.ValueAsString(localVal);
185 nsAutoString remoteVal;
186 aFields->GetAttribute(prop.Name(), remoteVal);
187 if (!localVal.Equals(remoteVal)) {
188 if (!mismatches) {
189 logging::MsgBegin("Mismatch!", "Local and remote values differ");
190 logging::AccessibleInfo("", acc);
191 mismatches = true;
193 nsAutoCString propName;
194 prop.Name()->ToUTF8String(propName);
195 logging::MsgEntry("Fields differ: %s '%s' != '%s'", propName.get(),
196 NS_ConvertUTF16toUTF8(remoteVal).get(),
197 NS_ConvertUTF16toUTF8(localVal).get());
200 if (mismatches) {
201 logging::MsgEnd();
203 #endif // A11Y_LOG
205 return IPC_OK();
208 mozilla::ipc::IPCResult DocAccessibleChild::RecvDoActionAsync(
209 const uint64_t& aID, const uint8_t& aIndex) {
210 if (LocalAccessible* acc = IdToAccessible(aID)) {
211 Unused << acc->DoAction(aIndex);
214 return IPC_OK();
217 mozilla::ipc::IPCResult DocAccessibleChild::RecvSetCaretOffset(
218 const uint64_t& aID, const int32_t& aOffset) {
219 HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
220 if (acc && acc->IsTextRole() && acc->IsValidOffset(aOffset)) {
221 acc->SetCaretOffset(aOffset);
223 return IPC_OK();
226 mozilla::ipc::IPCResult DocAccessibleChild::RecvSetTextSelection(
227 const uint64_t& aStartID, const int32_t& aStartOffset,
228 const uint64_t& aEndID, const int32_t& aEndOffset,
229 const int32_t& aSelectionNum) {
230 TextLeafRange range(TextLeafPoint(IdToAccessible(aStartID), aStartOffset),
231 TextLeafPoint(IdToAccessible(aEndID), aEndOffset));
232 if (range) {
233 range.SetSelection(aSelectionNum);
236 return IPC_OK();
239 mozilla::ipc::IPCResult DocAccessibleChild::RecvScrollTextLeafRangeIntoView(
240 const uint64_t& aStartID, const int32_t& aStartOffset,
241 const uint64_t& aEndID, const int32_t& aEndOffset,
242 const uint32_t& aScrollType) {
243 TextLeafRange range(TextLeafPoint(IdToAccessible(aStartID), aStartOffset),
244 TextLeafPoint(IdToAccessible(aEndID), aEndOffset));
245 if (range) {
246 range.ScrollIntoView(aScrollType);
249 return IPC_OK();
252 mozilla::ipc::IPCResult DocAccessibleChild::RecvRemoveTextSelection(
253 const uint64_t& aID, const int32_t& aSelectionNum) {
254 HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
255 if (acc && acc->IsTextRole()) {
256 acc->RemoveFromSelection(aSelectionNum);
259 return IPC_OK();
262 mozilla::ipc::IPCResult DocAccessibleChild::RecvSetCurValue(
263 const uint64_t& aID, const double& aValue) {
264 LocalAccessible* acc = IdToAccessible(aID);
265 if (acc) {
266 acc->SetCurValue(aValue);
269 return IPC_OK();
272 mozilla::ipc::IPCResult DocAccessibleChild::RecvReplaceText(
273 const uint64_t& aID, const nsAString& aText) {
274 HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
275 if (acc && acc->IsTextRole()) {
276 acc->ReplaceText(aText);
279 return IPC_OK();
282 mozilla::ipc::IPCResult DocAccessibleChild::RecvInsertText(
283 const uint64_t& aID, const nsAString& aText, const int32_t& aPosition) {
284 HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
285 if (acc && acc->IsTextRole()) {
286 acc->InsertText(aText, aPosition);
289 return IPC_OK();
292 mozilla::ipc::IPCResult DocAccessibleChild::RecvCopyText(
293 const uint64_t& aID, const int32_t& aStartPos, const int32_t& aEndPos) {
294 HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
295 if (acc && acc->IsTextRole()) {
296 acc->CopyText(aStartPos, aEndPos);
299 return IPC_OK();
302 mozilla::ipc::IPCResult DocAccessibleChild::RecvCutText(
303 const uint64_t& aID, const int32_t& aStartPos, const int32_t& aEndPos) {
304 HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
305 if (acc && acc->IsTextRole()) {
306 acc->CutText(aStartPos, aEndPos);
309 return IPC_OK();
312 mozilla::ipc::IPCResult DocAccessibleChild::RecvDeleteText(
313 const uint64_t& aID, const int32_t& aStartPos, const int32_t& aEndPos) {
314 HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
315 if (acc && acc->IsTextRole()) {
316 acc->DeleteText(aStartPos, aEndPos);
319 return IPC_OK();
322 mozilla::ipc::IPCResult DocAccessibleChild::RecvPasteText(
323 const uint64_t& aID, const int32_t& aPosition) {
324 RefPtr<HyperTextAccessible> acc = IdToHyperTextAccessible(aID);
325 if (acc && acc->IsTextRole()) {
326 acc->PasteText(aPosition);
329 return IPC_OK();
332 ipc::IPCResult DocAccessibleChild::RecvRestoreFocus() {
333 if (FocusManager* focusMgr = FocusMgr()) {
334 focusMgr->ForceFocusEvent();
336 return IPC_OK();
339 mozilla::ipc::IPCResult DocAccessibleChild::RecvScrollToPoint(
340 const uint64_t& aID, const uint32_t& aScrollType, const int32_t& aX,
341 const int32_t& aY) {
342 LocalAccessible* acc = IdToAccessible(aID);
343 if (acc) {
344 acc->ScrollToPoint(aScrollType, aX, aY);
347 return IPC_OK();
350 LayoutDeviceIntRect DocAccessibleChild::GetCaretRectFor(const uint64_t& aID) {
351 #if defined(XP_WIN)
352 LocalAccessible* target;
354 if (aID) {
355 target = reinterpret_cast<LocalAccessible*>(aID);
356 } else {
357 target = mDoc;
360 MOZ_ASSERT(target);
362 HyperTextAccessible* text = target->AsHyperText();
363 if (!text) {
364 return LayoutDeviceIntRect();
367 nsIWidget* widget = nullptr;
368 return text->GetCaretRect(&widget);
369 #else
370 // The caret rect is only used on Windows, so just return an empty rect
371 // on other platforms.
372 return LayoutDeviceIntRect();
373 #endif // defined(XP_WIN)
376 bool DocAccessibleChild::SendFocusEvent(const uint64_t& aID) {
377 return PDocAccessibleChild::SendFocusEvent(aID, GetCaretRectFor(aID));
380 bool DocAccessibleChild::SendCaretMoveEvent(const uint64_t& aID,
381 const int32_t& aOffset,
382 const bool& aIsSelectionCollapsed,
383 const bool& aIsAtEndOfLine,
384 const int32_t& aGranularity) {
385 return PDocAccessibleChild::SendCaretMoveEvent(aID, GetCaretRectFor(aID),
386 aOffset, aIsSelectionCollapsed,
387 aIsAtEndOfLine, aGranularity);
390 #if !defined(XP_WIN)
391 mozilla::ipc::IPCResult DocAccessibleChild::RecvAnnounce(
392 const uint64_t& aID, const nsAString& aAnnouncement,
393 const uint16_t& aPriority) {
394 LocalAccessible* acc = IdToAccessible(aID);
395 if (acc) {
396 acc->Announce(aAnnouncement, aPriority);
399 return IPC_OK();
401 #endif // !defined(XP_WIN)
403 mozilla::ipc::IPCResult DocAccessibleChild::RecvScrollSubstringToPoint(
404 const uint64_t& aID, const int32_t& aStartOffset, const int32_t& aEndOffset,
405 const uint32_t& aCoordinateType, const int32_t& aX, const int32_t& aY) {
406 HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
407 if (acc) {
408 acc->ScrollSubstringToPoint(aStartOffset, aEndOffset, aCoordinateType, aX,
409 aY);
412 return IPC_OK();
415 LocalAccessible* DocAccessibleChild::IdToAccessible(const uint64_t& aID) const {
416 if (!aID) return mDoc;
418 if (!mDoc) return nullptr;
420 return mDoc->GetAccessibleByUniqueID(reinterpret_cast<void*>(aID));
423 HyperTextAccessible* DocAccessibleChild::IdToHyperTextAccessible(
424 const uint64_t& aID) const {
425 LocalAccessible* acc = IdToAccessible(aID);
426 return acc && acc->IsHyperText() ? acc->AsHyperText() : nullptr;
429 } // namespace a11y
430 } // namespace mozilla