Bug 1681980 [wpt PR 26856] - Update wpt metadata, a=testonly
[gecko.git] / accessible / android / DocAccessibleWrap.cpp
bloba9652c1e1b0cfc41ac7e2ae7e8fcf46ec307608f
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 "Accessible-inl.h"
8 #include "AccessibleOrProxy.h"
9 #include "DocAccessibleChild.h"
10 #include "DocAccessibleWrap.h"
11 #include "nsIDocShell.h"
12 #include "nsIScrollableFrame.h"
13 #include "nsLayoutUtils.h"
14 #include "nsAccessibilityService.h"
15 #include "nsAccUtils.h"
16 #include "nsIPersistentProperties2.h"
17 #include "Pivot.h"
18 #include "SessionAccessibility.h"
19 #include "TraversalRule.h"
20 #include "mozilla/PresShell.h"
21 #include "mozilla/a11y/DocAccessiblePlatformExtChild.h"
23 using namespace mozilla;
24 using namespace mozilla::a11y;
26 const uint32_t kCacheRefreshInterval = 500;
28 #define UNIQUE_ID(acc) \
29 !acc || (acc->IsDoc() && acc->AsDoc()->IPCDoc()) \
30 ? 0 \
31 : reinterpret_cast<uint64_t>(acc->UniqueID())
33 ////////////////////////////////////////////////////////////////////////////////
34 // DocAccessibleWrap
35 ////////////////////////////////////////////////////////////////////////////////
37 DocAccessibleWrap::DocAccessibleWrap(Document* aDocument, PresShell* aPresShell)
38 : DocAccessible(aDocument, aPresShell) {
39 if (aDocument->GetBrowsingContext()->IsTopContent()) {
40 // The top-level content document gets this special ID.
41 mID = kNoID;
42 } else {
43 mID = AcquireID();
47 DocAccessibleWrap::~DocAccessibleWrap() {}
49 AccessibleWrap* DocAccessibleWrap::GetAccessibleByID(int32_t aID) const {
50 if (AccessibleWrap* acc = mIDToAccessibleMap.Get(aID)) {
51 return acc;
54 // If the ID is not in the hash table, check the IDs of the child docs.
55 for (uint32_t i = 0; i < ChildDocumentCount(); i++) {
56 auto childDoc = reinterpret_cast<AccessibleWrap*>(GetChildDocumentAt(i));
57 if (childDoc->VirtualViewID() == aID) {
58 return childDoc;
62 return nullptr;
65 void DocAccessibleWrap::DoInitialUpdate() {
66 DocAccessible::DoInitialUpdate();
67 CacheViewport(true);
70 nsresult DocAccessibleWrap::HandleAccEvent(AccEvent* aEvent) {
71 switch (aEvent->GetEventType()) {
72 case nsIAccessibleEvent::EVENT_SCROLLING_END:
73 CacheViewport(false);
74 break;
75 case nsIAccessibleEvent::EVENT_SCROLLING:
76 UpdateFocusPathBounds();
77 break;
78 default:
79 break;
82 return DocAccessible::HandleAccEvent(aEvent);
85 void DocAccessibleWrap::CacheViewportCallback(nsITimer* aTimer,
86 void* aDocAccParam) {
87 RefPtr<DocAccessibleWrap> docAcc(
88 dont_AddRef(reinterpret_cast<DocAccessibleWrap*>(aDocAccParam)));
89 if (!docAcc || docAcc->HasShutdown()) {
90 return;
93 PresShell* presShell = docAcc->PresShellPtr();
94 nsIFrame* rootFrame = presShell->GetRootFrame();
95 if (!rootFrame) {
96 return;
99 nsTArray<nsIFrame*> frames;
100 nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
101 nsRect scrollPort = sf ? sf->GetScrollPortRect() : rootFrame->GetRect();
103 nsLayoutUtils::GetFramesForArea(
104 RelativeTo{presShell->GetRootFrame()}, scrollPort, frames,
105 {nsLayoutUtils::FrameForPointOption::OnlyVisible});
106 AccessibleHashtable inViewAccs;
107 for (size_t i = 0; i < frames.Length(); i++) {
108 nsIContent* content = frames.ElementAt(i)->GetContent();
109 if (!content) {
110 continue;
113 Accessible* visibleAcc = docAcc->GetAccessibleOrContainer(content);
114 if (!visibleAcc) {
115 continue;
118 for (Accessible* acc = visibleAcc; acc && acc != docAcc->Parent();
119 acc = acc->Parent()) {
120 if (inViewAccs.Contains(acc->UniqueID())) {
121 break;
123 inViewAccs.Put(acc->UniqueID(), RefPtr{acc});
127 if (IPCAccessibilityActive()) {
128 DocAccessibleChild* ipcDoc = docAcc->IPCDoc();
129 nsTArray<BatchData> cacheData(inViewAccs.Count());
130 for (auto iter = inViewAccs.Iter(); !iter.Done(); iter.Next()) {
131 Accessible* accessible = iter.Data();
132 nsAutoString name;
133 accessible->Name(name);
134 nsAutoString textValue;
135 accessible->Value(textValue);
136 nsAutoString nodeID;
137 static_cast<AccessibleWrap*>(accessible)->WrapperDOMNodeID(nodeID);
138 nsAutoString description;
139 accessible->Description(description);
141 cacheData.AppendElement(BatchData(
142 accessible->Document()->IPCDoc(), UNIQUE_ID(accessible),
143 accessible->State(), accessible->Bounds(), accessible->ActionCount(),
144 name, textValue, nodeID, description, UnspecifiedNaN<double>(),
145 UnspecifiedNaN<double>(), UnspecifiedNaN<double>(),
146 UnspecifiedNaN<double>(), nsTArray<Attribute>()));
149 ipcDoc->SendBatch(eBatch_Viewport, cacheData);
150 } else if (RefPtr<SessionAccessibility> sessionAcc =
151 SessionAccessibility::GetInstanceFor(docAcc)) {
152 nsTArray<AccessibleWrap*> accessibles(inViewAccs.Count());
153 for (auto iter = inViewAccs.Iter(); !iter.Done(); iter.Next()) {
154 accessibles.AppendElement(
155 static_cast<AccessibleWrap*>(iter.Data().get()));
158 sessionAcc->ReplaceViewportCache(accessibles);
161 if (docAcc->mCachePivotBoundaries) {
162 AccessibleOrProxy accOrProxy = AccessibleOrProxy(docAcc);
163 a11y::Pivot pivot(accOrProxy);
164 TraversalRule rule(java::SessionAccessibility::HTML_GRANULARITY_DEFAULT);
165 Accessible* first = pivot.First(rule).AsAccessible();
166 Accessible* last = pivot.Last(rule).AsAccessible();
168 // If first/last are null, pass the root document as pivot boundary.
169 if (IPCAccessibilityActive()) {
170 DocAccessibleChild* ipcDoc = docAcc->IPCDoc();
171 DocAccessibleChild* firstDoc =
172 first ? first->Document()->IPCDoc() : ipcDoc;
173 DocAccessibleChild* lastDoc = last ? last->Document()->IPCDoc() : ipcDoc;
174 if (ipcDoc && firstDoc && lastDoc) {
175 // One or more of the documents may not have recieved an IPC doc yet.
176 // In that case, just throw away this update. We will get a new one soon
177 // enough.
178 ipcDoc->GetPlatformExtension()->SendSetPivotBoundaries(
179 firstDoc, UNIQUE_ID(first), lastDoc, UNIQUE_ID(last));
181 } else if (RefPtr<SessionAccessibility> sessionAcc =
182 SessionAccessibility::GetInstanceFor(docAcc)) {
183 sessionAcc->UpdateAccessibleFocusBoundaries(
184 first ? static_cast<AccessibleWrap*>(first) : docAcc,
185 last ? static_cast<AccessibleWrap*>(last) : docAcc);
188 docAcc->mCachePivotBoundaries = false;
191 if (docAcc->mCacheRefreshTimer) {
192 docAcc->mCacheRefreshTimer = nullptr;
196 void DocAccessibleWrap::CacheViewport(bool aCachePivotBoundaries) {
197 mCachePivotBoundaries |= aCachePivotBoundaries;
198 if (VirtualViewID() == kNoID && !mCacheRefreshTimer) {
199 NS_NewTimerWithFuncCallback(getter_AddRefs(mCacheRefreshTimer),
200 CacheViewportCallback, this,
201 kCacheRefreshInterval, nsITimer::TYPE_ONE_SHOT,
202 "a11y::DocAccessibleWrap::CacheViewport");
203 if (mCacheRefreshTimer) {
204 NS_ADDREF_THIS(); // Kung fu death grip
209 DocAccessibleWrap* DocAccessibleWrap::GetTopLevelContentDoc(
210 AccessibleWrap* aAccessible) {
211 DocAccessibleWrap* doc =
212 static_cast<DocAccessibleWrap*>(aAccessible->Document());
213 while (doc && doc->VirtualViewID() != kNoID) {
214 doc = static_cast<DocAccessibleWrap*>(doc->ParentDocument());
217 return doc;
220 void DocAccessibleWrap::CacheFocusPath(AccessibleWrap* aAccessible) {
221 mFocusPath.Clear();
222 if (IPCAccessibilityActive()) {
223 DocAccessibleChild* ipcDoc = IPCDoc();
224 nsTArray<BatchData> cacheData;
225 for (AccessibleWrap* acc = aAccessible; acc && acc != this->Parent();
226 acc = static_cast<AccessibleWrap*>(acc->Parent())) {
227 nsAutoString name;
228 acc->Name(name);
229 nsAutoString textValue;
230 acc->Value(textValue);
231 nsAutoString nodeID;
232 acc->WrapperDOMNodeID(nodeID);
233 nsAutoString description;
234 acc->Description(description);
235 nsCOMPtr<nsIPersistentProperties> props = acc->Attributes();
236 nsTArray<Attribute> attributes;
237 nsAccUtils::PersistentPropertiesToArray(props, &attributes);
238 cacheData.AppendElement(
239 BatchData(acc->Document()->IPCDoc(), UNIQUE_ID(acc), acc->State(),
240 acc->Bounds(), acc->ActionCount(), name, textValue, nodeID,
241 description, acc->CurValue(), acc->MinValue(),
242 acc->MaxValue(), acc->Step(), attributes));
243 mFocusPath.Put(acc->UniqueID(), RefPtr{acc});
246 ipcDoc->SendBatch(eBatch_FocusPath, cacheData);
247 } else if (RefPtr<SessionAccessibility> sessionAcc =
248 SessionAccessibility::GetInstanceFor(this)) {
249 nsTArray<AccessibleWrap*> accessibles;
250 for (AccessibleWrap* acc = aAccessible; acc && acc != this->Parent();
251 acc = static_cast<AccessibleWrap*>(acc->Parent())) {
252 accessibles.AppendElement(acc);
253 mFocusPath.Put(acc->UniqueID(), RefPtr{acc});
256 sessionAcc->ReplaceFocusPathCache(accessibles);
260 void DocAccessibleWrap::UpdateFocusPathBounds() {
261 if (!mFocusPath.Count()) {
262 return;
265 if (IPCAccessibilityActive()) {
266 DocAccessibleChild* ipcDoc = IPCDoc();
267 nsTArray<BatchData> boundsData(mFocusPath.Count());
268 for (auto iter = mFocusPath.Iter(); !iter.Done(); iter.Next()) {
269 Accessible* accessible = iter.Data();
270 if (!accessible || accessible->IsDefunct()) {
271 MOZ_ASSERT_UNREACHABLE("Focus path cached accessible is gone.");
272 continue;
275 boundsData.AppendElement(
276 BatchData(accessible->Document()->IPCDoc(), UNIQUE_ID(accessible), 0,
277 accessible->Bounds(), 0, nsString(), nsString(), nsString(),
278 nsString(), UnspecifiedNaN<double>(),
279 UnspecifiedNaN<double>(), UnspecifiedNaN<double>(),
280 UnspecifiedNaN<double>(), nsTArray<Attribute>()));
283 ipcDoc->SendBatch(eBatch_BoundsUpdate, boundsData);
284 } else if (RefPtr<SessionAccessibility> sessionAcc =
285 SessionAccessibility::GetInstanceFor(this)) {
286 nsTArray<AccessibleWrap*> accessibles(mFocusPath.Count());
287 for (auto iter = mFocusPath.Iter(); !iter.Done(); iter.Next()) {
288 Accessible* accessible = iter.Data();
289 if (!accessible || accessible->IsDefunct()) {
290 MOZ_ASSERT_UNREACHABLE("Focus path cached accessible is gone.");
291 continue;
294 accessibles.AppendElement(static_cast<AccessibleWrap*>(accessible));
297 sessionAcc->UpdateCachedBounds(accessibles);
301 #undef UNIQUE_ID