Source code

Revision control

Other Tools

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/. */
6
7
#include "Accessible-inl.h"
8
#include "DocAccessibleWrap.h"
9
#include "nsIDocShell.h"
10
#include "nsLayoutUtils.h"
11
#include "DocAccessibleChild.h"
12
#include "nsAccessibilityService.h"
13
#include "nsAccUtils.h"
14
#include "nsIPersistentProperties2.h"
15
#include "SessionAccessibility.h"
16
#include "mozilla/PresShell.h"
17
18
using namespace mozilla;
19
using namespace mozilla::a11y;
20
21
const uint32_t kCacheRefreshInterval = 500;
22
23
////////////////////////////////////////////////////////////////////////////////
24
// DocAccessibleWrap
25
////////////////////////////////////////////////////////////////////////////////
26
27
DocAccessibleWrap::DocAccessibleWrap(Document* aDocument, PresShell* aPresShell)
28
: DocAccessible(aDocument, aPresShell) {
29
nsCOMPtr<nsIDocShellTreeItem> treeItem(aDocument->GetDocShell());
30
31
nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
32
treeItem->GetParent(getter_AddRefs(parentTreeItem));
33
34
if (treeItem->ItemType() == nsIDocShellTreeItem::typeContent &&
35
(!parentTreeItem ||
36
parentTreeItem->ItemType() == nsIDocShellTreeItem::typeChrome)) {
37
// The top-level content document gets this special ID.
38
mID = kNoID;
39
} else {
40
mID = AcquireID();
41
}
42
}
43
44
DocAccessibleWrap::~DocAccessibleWrap() {}
45
46
AccessibleWrap* DocAccessibleWrap::GetAccessibleByID(int32_t aID) const {
47
if (AccessibleWrap* acc = mIDToAccessibleMap.Get(aID)) {
48
return acc;
49
}
50
51
// If the ID is not in the hash table, check the IDs of the child docs.
52
for (uint32_t i = 0; i < ChildDocumentCount(); i++) {
53
auto childDoc = reinterpret_cast<AccessibleWrap*>(GetChildDocumentAt(i));
54
if (childDoc->VirtualViewID() == aID) {
55
return childDoc;
56
}
57
}
58
59
return nullptr;
60
}
61
62
void DocAccessibleWrap::DoInitialUpdate() {
63
DocAccessible::DoInitialUpdate();
64
CacheViewport();
65
}
66
67
nsresult DocAccessibleWrap::HandleAccEvent(AccEvent* aEvent) {
68
switch (aEvent->GetEventType()) {
69
case nsIAccessibleEvent::EVENT_SHOW:
70
case nsIAccessibleEvent::EVENT_HIDE:
71
case nsIAccessibleEvent::EVENT_SCROLLING_END:
72
CacheViewport();
73
break;
74
case nsIAccessibleEvent::EVENT_SCROLLING:
75
UpdateFocusPathBounds();
76
break;
77
default:
78
break;
79
}
80
81
return DocAccessible::HandleAccEvent(aEvent);
82
}
83
84
void DocAccessibleWrap::CacheViewportCallback(nsITimer* aTimer,
85
void* aDocAccParam) {
86
RefPtr<DocAccessibleWrap> docAcc(
87
dont_AddRef(reinterpret_cast<DocAccessibleWrap*>(aDocAccParam)));
88
if (!docAcc || docAcc->HasShutdown()) {
89
return;
90
}
91
92
PresShell* presShell = docAcc->PresShellPtr();
93
nsIFrame* rootFrame = presShell->GetRootFrame();
94
if (!rootFrame) {
95
return;
96
}
97
98
nsTArray<nsIFrame*> frames;
99
nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
100
nsRect scrollPort = sf ? sf->GetScrollPortRect() : rootFrame->GetRect();
101
102
nsLayoutUtils::GetFramesForArea(
103
presShell->GetRootFrame(), scrollPort, frames,
104
nsLayoutUtils::FrameForPointOption::OnlyVisible);
105
AccessibleHashtable inViewAccs;
106
for (size_t i = 0; i < frames.Length(); i++) {
107
nsIContent* content = frames.ElementAt(i)->GetContent();
108
if (!content) {
109
continue;
110
}
111
112
Accessible* visibleAcc = docAcc->GetAccessibleOrContainer(content);
113
if (!visibleAcc) {
114
continue;
115
}
116
117
for (Accessible* acc = visibleAcc; acc && acc != docAcc->Parent();
118
acc = acc->Parent()) {
119
if (inViewAccs.Contains(acc->UniqueID())) {
120
break;
121
}
122
inViewAccs.Put(acc->UniqueID(), acc);
123
}
124
}
125
126
if (IPCAccessibilityActive()) {
127
DocAccessibleChild* ipcDoc = docAcc->IPCDoc();
128
nsTArray<BatchData> cacheData(inViewAccs.Count());
129
for (auto iter = inViewAccs.Iter(); !iter.Done(); iter.Next()) {
130
Accessible* accessible = iter.Data();
131
auto uid = accessible->IsDoc() && accessible->AsDoc()->IPCDoc()
132
? 0
133
: reinterpret_cast<uint64_t>(accessible->UniqueID());
134
135
nsAutoString name;
136
accessible->Name(name);
137
nsAutoString textValue;
138
accessible->Value(textValue);
139
nsAutoString nodeID;
140
static_cast<AccessibleWrap*>(accessible)->WrapperDOMNodeID(nodeID);
141
nsAutoString description;
142
accessible->Description(description);
143
144
cacheData.AppendElement(
145
BatchData(accessible->Document()->IPCDoc(), uid, accessible->State(),
146
accessible->Bounds(), accessible->ActionCount(), name,
147
textValue, nodeID, description, UnspecifiedNaN<double>(),
148
UnspecifiedNaN<double>(), UnspecifiedNaN<double>(),
149
UnspecifiedNaN<double>(), nsTArray<Attribute>()));
150
}
151
152
ipcDoc->SendBatch(eBatch_Viewport, cacheData);
153
} else if (SessionAccessibility* sessionAcc =
154
SessionAccessibility::GetInstanceFor(docAcc)) {
155
nsTArray<AccessibleWrap*> accessibles(inViewAccs.Count());
156
for (auto iter = inViewAccs.Iter(); !iter.Done(); iter.Next()) {
157
accessibles.AppendElement(
158
static_cast<AccessibleWrap*>(iter.Data().get()));
159
}
160
161
sessionAcc->ReplaceViewportCache(accessibles);
162
}
163
164
if (docAcc->mCacheRefreshTimer) {
165
docAcc->mCacheRefreshTimer = nullptr;
166
}
167
}
168
169
void DocAccessibleWrap::CacheViewport() {
170
if (VirtualViewID() == kNoID && !mCacheRefreshTimer) {
171
NS_NewTimerWithFuncCallback(getter_AddRefs(mCacheRefreshTimer),
172
CacheViewportCallback, this,
173
kCacheRefreshInterval, nsITimer::TYPE_ONE_SHOT,
174
"a11y::DocAccessibleWrap::CacheViewport");
175
if (mCacheRefreshTimer) {
176
NS_ADDREF_THIS(); // Kung fu death grip
177
}
178
}
179
}
180
181
DocAccessibleWrap* DocAccessibleWrap::GetTopLevelContentDoc(
182
AccessibleWrap* aAccessible) {
183
DocAccessibleWrap* doc =
184
static_cast<DocAccessibleWrap*>(aAccessible->Document());
185
while (doc && doc->VirtualViewID() != kNoID) {
186
doc = static_cast<DocAccessibleWrap*>(doc->ParentDocument());
187
}
188
189
return doc;
190
}
191
192
void DocAccessibleWrap::CacheFocusPath(AccessibleWrap* aAccessible) {
193
mFocusPath.Clear();
194
if (IPCAccessibilityActive()) {
195
DocAccessibleChild* ipcDoc = IPCDoc();
196
nsTArray<BatchData> cacheData;
197
for (AccessibleWrap* acc = aAccessible; acc && acc != this->Parent();
198
acc = static_cast<AccessibleWrap*>(acc->Parent())) {
199
auto uid = acc->IsDoc() && acc->AsDoc()->IPCDoc()
200
? 0
201
: reinterpret_cast<uint64_t>(acc->UniqueID());
202
nsAutoString name;
203
acc->Name(name);
204
nsAutoString textValue;
205
acc->Value(textValue);
206
nsAutoString nodeID;
207
acc->WrapperDOMNodeID(nodeID);
208
nsAutoString description;
209
acc->Description(description);
210
nsCOMPtr<nsIPersistentProperties> props = acc->Attributes();
211
nsTArray<Attribute> attributes;
212
nsAccUtils::PersistentPropertiesToArray(props, &attributes);
213
cacheData.AppendElement(
214
BatchData(acc->Document()->IPCDoc(), uid, acc->State(), acc->Bounds(),
215
acc->ActionCount(), name, textValue, nodeID, description,
216
acc->CurValue(), acc->MinValue(), acc->MaxValue(),
217
acc->Step(), attributes));
218
mFocusPath.Put(acc->UniqueID(), acc);
219
}
220
221
ipcDoc->SendBatch(eBatch_FocusPath, cacheData);
222
} else if (SessionAccessibility* sessionAcc =
223
SessionAccessibility::GetInstanceFor(this)) {
224
nsTArray<AccessibleWrap*> accessibles;
225
for (AccessibleWrap* acc = aAccessible; acc && acc != this->Parent();
226
acc = static_cast<AccessibleWrap*>(acc->Parent())) {
227
accessibles.AppendElement(acc);
228
mFocusPath.Put(acc->UniqueID(), acc);
229
}
230
231
sessionAcc->ReplaceFocusPathCache(accessibles);
232
}
233
}
234
235
void DocAccessibleWrap::UpdateFocusPathBounds() {
236
if (!mFocusPath.Count()) {
237
return;
238
}
239
240
if (IPCAccessibilityActive()) {
241
DocAccessibleChild* ipcDoc = IPCDoc();
242
nsTArray<BatchData> boundsData(mFocusPath.Count());
243
for (auto iter = mFocusPath.Iter(); !iter.Done(); iter.Next()) {
244
Accessible* accessible = iter.Data();
245
if (!accessible || accessible->IsDefunct()) {
246
MOZ_ASSERT_UNREACHABLE("Focus path cached accessible is gone.");
247
continue;
248
}
249
250
auto uid = accessible->IsDoc() && accessible->AsDoc()->IPCDoc()
251
? 0
252
: reinterpret_cast<uint64_t>(accessible->UniqueID());
253
boundsData.AppendElement(
254
BatchData(accessible->Document()->IPCDoc(), uid, 0,
255
accessible->Bounds(), 0, nsString(), nsString(), nsString(),
256
nsString(), UnspecifiedNaN<double>(),
257
UnspecifiedNaN<double>(), UnspecifiedNaN<double>(),
258
UnspecifiedNaN<double>(), nsTArray<Attribute>()));
259
}
260
261
ipcDoc->SendBatch(eBatch_BoundsUpdate, boundsData);
262
} else if (SessionAccessibility* sessionAcc =
263
SessionAccessibility::GetInstanceFor(this)) {
264
nsTArray<AccessibleWrap*> accessibles(mFocusPath.Count());
265
for (auto iter = mFocusPath.Iter(); !iter.Done(); iter.Next()) {
266
Accessible* accessible = iter.Data();
267
if (!accessible || accessible->IsDefunct()) {
268
MOZ_ASSERT_UNREACHABLE("Focus path cached accessible is gone.");
269
continue;
270
}
271
272
accessibles.AppendElement(static_cast<AccessibleWrap*>(accessible));
273
}
274
275
sessionAcc->UpdateCachedBounds(accessibles);
276
}
277
}