Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
* License, v. 2.0. If a copy of the MPL was not distributed with this
4
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "AccessibleWrap.h"
7
8
#include "Accessible-inl.h"
9
#include "AndroidInputType.h"
10
#include "DocAccessibleWrap.h"
11
#include "IDSet.h"
12
#include "JavaBuiltins.h"
13
#include "SessionAccessibility.h"
14
#include "nsAccessibilityService.h"
15
#include "nsPersistentProperties.h"
16
#include "nsIAccessibleAnnouncementEvent.h"
17
#include "nsIStringBundle.h"
18
#include "nsAccUtils.h"
19
#include "nsTextEquivUtils.h"
20
21
#include "mozilla/a11y/PDocAccessibleChild.h"
22
#include "mozilla/jni/GeckoBundleUtils.h"
23
24
#define ROLE_STRINGS_URL "chrome://global/locale/AccessFu.properties"
25
26
using namespace mozilla::a11y;
27
28
// IDs should be a positive 32bit integer.
29
IDSet sIDSet(31UL);
30
31
//-----------------------------------------------------
32
// construction
33
//-----------------------------------------------------
34
AccessibleWrap::AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc)
35
: Accessible(aContent, aDoc) {
36
if (aDoc) {
37
mID = AcquireID();
38
DocAccessibleWrap* doc = static_cast<DocAccessibleWrap*>(aDoc);
39
doc->AddID(mID, this);
40
}
41
}
42
43
//-----------------------------------------------------
44
// destruction
45
//-----------------------------------------------------
46
AccessibleWrap::~AccessibleWrap() {}
47
48
nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) {
49
auto accessible = static_cast<AccessibleWrap*>(aEvent->GetAccessible());
50
NS_ENSURE_TRUE(accessible, NS_ERROR_FAILURE);
51
DocAccessibleWrap* doc =
52
static_cast<DocAccessibleWrap*>(accessible->Document());
53
if (doc) {
54
switch (aEvent->GetEventType()) {
55
case nsIAccessibleEvent::EVENT_FOCUS: {
56
if (DocAccessibleWrap* topContentDoc =
57
doc->GetTopLevelContentDoc(accessible)) {
58
topContentDoc->CacheFocusPath(accessible);
59
}
60
break;
61
}
62
case nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED: {
63
AccVCChangeEvent* vcEvent = downcast_accEvent(aEvent);
64
auto newPosition =
65
static_cast<AccessibleWrap*>(vcEvent->NewAccessible());
66
if (newPosition) {
67
if (DocAccessibleWrap* topContentDoc =
68
doc->GetTopLevelContentDoc(accessible)) {
69
topContentDoc->CacheFocusPath(newPosition);
70
}
71
}
72
break;
73
}
74
case nsIAccessibleEvent::EVENT_SHOW:
75
case nsIAccessibleEvent::EVENT_HIDE: {
76
if (DocAccessibleWrap* topContentDoc =
77
doc->GetTopLevelContentDoc(accessible)) {
78
topContentDoc->CacheViewport();
79
}
80
break;
81
}
82
default:
83
break;
84
}
85
}
86
87
nsresult rv = Accessible::HandleAccEvent(aEvent);
88
NS_ENSURE_SUCCESS(rv, rv);
89
90
accessible->HandleLiveRegionEvent(aEvent);
91
92
if (IPCAccessibilityActive()) {
93
return NS_OK;
94
}
95
96
// The accessible can become defunct if we have an xpcom event listener
97
// which decides it would be fun to change the DOM and flush layout.
98
if (accessible->IsDefunct() || !accessible->IsBoundToParent()) {
99
return NS_OK;
100
}
101
102
if (doc) {
103
if (!nsCoreUtils::IsContentDocument(doc->DocumentNode())) {
104
return NS_OK;
105
}
106
}
107
108
RefPtr<SessionAccessibility> sessionAcc =
109
SessionAccessibility::GetInstanceFor(accessible);
110
if (!sessionAcc) {
111
return NS_OK;
112
}
113
114
switch (aEvent->GetEventType()) {
115
case nsIAccessibleEvent::EVENT_FOCUS:
116
sessionAcc->SendFocusEvent(accessible);
117
break;
118
case nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED: {
119
AccVCChangeEvent* vcEvent = downcast_accEvent(aEvent);
120
RefPtr<AccessibleWrap> newPosition =
121
static_cast<AccessibleWrap*>(vcEvent->NewAccessible());
122
auto oldPosition = static_cast<AccessibleWrap*>(vcEvent->OldAccessible());
123
124
if (sessionAcc && newPosition) {
125
if (oldPosition != newPosition) {
126
if (vcEvent->Reason() == nsIAccessiblePivot::REASON_POINT) {
127
sessionAcc->SendHoverEnterEvent(newPosition);
128
} else {
129
sessionAcc->SendAccessibilityFocusedEvent(newPosition);
130
}
131
}
132
133
if (vcEvent->BoundaryType() != nsIAccessiblePivot::NO_BOUNDARY) {
134
sessionAcc->SendTextTraversedEvent(
135
newPosition, vcEvent->NewStartOffset(), vcEvent->NewEndOffset());
136
}
137
}
138
break;
139
}
140
case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED: {
141
AccCaretMoveEvent* event = downcast_accEvent(aEvent);
142
sessionAcc->SendTextSelectionChangedEvent(accessible,
143
event->GetCaretOffset());
144
break;
145
}
146
case nsIAccessibleEvent::EVENT_TEXT_INSERTED:
147
case nsIAccessibleEvent::EVENT_TEXT_REMOVED: {
148
AccTextChangeEvent* event = downcast_accEvent(aEvent);
149
sessionAcc->SendTextChangedEvent(
150
accessible, event->ModifiedText(), event->GetStartOffset(),
151
event->GetLength(), event->IsTextInserted(),
152
event->IsFromUserInput());
153
break;
154
}
155
case nsIAccessibleEvent::EVENT_STATE_CHANGE: {
156
AccStateChangeEvent* event = downcast_accEvent(aEvent);
157
auto state = event->GetState();
158
if (state & states::CHECKED) {
159
sessionAcc->SendClickedEvent(accessible, event->IsStateEnabled());
160
}
161
162
if (state & states::SELECTED) {
163
sessionAcc->SendSelectedEvent(accessible, event->IsStateEnabled());
164
}
165
166
if (state & states::BUSY) {
167
sessionAcc->SendWindowStateChangedEvent(accessible);
168
}
169
break;
170
}
171
case nsIAccessibleEvent::EVENT_SCROLLING: {
172
AccScrollingEvent* event = downcast_accEvent(aEvent);
173
sessionAcc->SendScrollingEvent(accessible, event->ScrollX(),
174
event->ScrollY(), event->MaxScrollX(),
175
event->MaxScrollY());
176
break;
177
}
178
case nsIAccessibleEvent::EVENT_ANNOUNCEMENT: {
179
AccAnnouncementEvent* event = downcast_accEvent(aEvent);
180
sessionAcc->SendAnnouncementEvent(accessible, event->Announcement(),
181
event->Priority());
182
break;
183
}
184
default:
185
break;
186
}
187
188
return NS_OK;
189
}
190
191
void AccessibleWrap::Shutdown() {
192
if (mDoc) {
193
if (mID > 0) {
194
if (auto doc = static_cast<DocAccessibleWrap*>(mDoc.get())) {
195
doc->RemoveID(mID);
196
}
197
ReleaseID(mID);
198
mID = 0;
199
}
200
}
201
202
Accessible::Shutdown();
203
}
204
205
bool AccessibleWrap::DoAction(uint8_t aIndex) const {
206
if (ActionCount()) {
207
return Accessible::DoAction(aIndex);
208
}
209
210
if (mContent) {
211
// We still simulate a click on an accessible even if there is no
212
// known actions. For the sake of bad markup.
213
DoCommand();
214
return true;
215
}
216
217
return false;
218
}
219
220
int32_t AccessibleWrap::AcquireID() { return sIDSet.GetID(); }
221
222
void AccessibleWrap::ReleaseID(int32_t aID) { sIDSet.ReleaseID(aID); }
223
224
void AccessibleWrap::SetTextContents(const nsAString& aText) {
225
if (IsHyperText()) {
226
AsHyperText()->ReplaceText(aText);
227
}
228
}
229
230
void AccessibleWrap::GetTextContents(nsAString& aText) {
231
// For now it is a simple wrapper for getting entire range of TextSubstring.
232
// In the future this may be smarter and retrieve a flattened string.
233
if (IsHyperText()) {
234
AsHyperText()->TextSubstring(0, -1, aText);
235
}
236
}
237
238
bool AccessibleWrap::GetSelectionBounds(int32_t* aStartOffset,
239
int32_t* aEndOffset) {
240
if (IsHyperText()) {
241
return AsHyperText()->SelectionBoundsAt(0, aStartOffset, aEndOffset);
242
}
243
244
return false;
245
}
246
247
uint32_t AccessibleWrap::GetFlags(role aRole, uint64_t aState,
248
uint8_t aActionCount) {
249
uint32_t flags = 0;
250
if (aState & states::CHECKABLE) {
251
flags |= java::SessionAccessibility::FLAG_CHECKABLE;
252
}
253
254
if (aState & states::CHECKED) {
255
flags |= java::SessionAccessibility::FLAG_CHECKED;
256
}
257
258
if (aState & states::INVALID) {
259
flags |= java::SessionAccessibility::FLAG_CONTENT_INVALID;
260
}
261
262
if (aState & states::EDITABLE) {
263
flags |= java::SessionAccessibility::FLAG_EDITABLE;
264
}
265
266
if (aActionCount && aRole != roles::TEXT_LEAF) {
267
flags |= java::SessionAccessibility::FLAG_CLICKABLE;
268
}
269
270
if (aState & states::ENABLED) {
271
flags |= java::SessionAccessibility::FLAG_ENABLED;
272
}
273
274
if (aState & states::FOCUSABLE) {
275
flags |= java::SessionAccessibility::FLAG_FOCUSABLE;
276
}
277
278
if (aState & states::FOCUSED) {
279
flags |= java::SessionAccessibility::FLAG_FOCUSED;
280
}
281
282
if (aState & states::MULTI_LINE) {
283
flags |= java::SessionAccessibility::FLAG_MULTI_LINE;
284
}
285
286
if (aState & states::SELECTABLE) {
287
flags |= java::SessionAccessibility::FLAG_SELECTABLE;
288
}
289
290
if (aState & states::SELECTED) {
291
flags |= java::SessionAccessibility::FLAG_SELECTED;
292
}
293
294
if ((aState & (states::INVISIBLE | states::OFFSCREEN)) == 0) {
295
flags |= java::SessionAccessibility::FLAG_VISIBLE_TO_USER;
296
}
297
298
if (aRole == roles::PASSWORD_TEXT) {
299
flags |= java::SessionAccessibility::FLAG_PASSWORD;
300
}
301
302
return flags;
303
}
304
305
void AccessibleWrap::GetRoleDescription(role aRole,
306
nsIPersistentProperties* aAttributes,
307
nsAString& aGeckoRole,
308
nsAString& aRoleDescription) {
309
nsresult rv = NS_OK;
310
311
nsCOMPtr<nsIStringBundleService> sbs =
312
do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
313
if (NS_FAILED(rv)) {
314
NS_WARNING("Failed to get string bundle service");
315
return;
316
}
317
318
nsCOMPtr<nsIStringBundle> bundle;
319
rv = sbs->CreateBundle(ROLE_STRINGS_URL, getter_AddRefs(bundle));
320
if (NS_FAILED(rv)) {
321
NS_WARNING("Failed to get string bundle");
322
return;
323
}
324
325
if (aRole == roles::HEADING && aAttributes) {
326
// The heading level is an attribute, so we need that.
327
AutoTArray<nsString, 1> formatString;
328
rv = aAttributes->GetStringProperty(NS_LITERAL_CSTRING("level"),
329
*formatString.AppendElement());
330
if (NS_SUCCEEDED(rv)) {
331
rv = bundle->FormatStringFromName("headingLevel", formatString,
332
aRoleDescription);
333
if (NS_SUCCEEDED(rv)) {
334
return;
335
}
336
}
337
}
338
339
GetAccService()->GetStringRole(aRole, aGeckoRole);
340
rv = bundle->GetStringFromName(NS_ConvertUTF16toUTF8(aGeckoRole).get(),
341
aRoleDescription);
342
if (NS_FAILED(rv)) {
343
aRoleDescription.AssignLiteral("");
344
}
345
}
346
347
already_AddRefed<nsIPersistentProperties>
348
AccessibleWrap::AttributeArrayToProperties(
349
const nsTArray<Attribute>& aAttributes) {
350
RefPtr<nsPersistentProperties> props = new nsPersistentProperties();
351
nsAutoString unused;
352
353
for (size_t i = 0; i < aAttributes.Length(); i++) {
354
props->SetStringProperty(aAttributes.ElementAt(i).Name(),
355
aAttributes.ElementAt(i).Value(), unused);
356
}
357
358
return props.forget();
359
}
360
361
int32_t AccessibleWrap::GetAndroidClass(role aRole) {
362
#define ROLE(geckoRole, stringRole, atkRole, macRole, msaaRole, ia2Role, \
363
androidClass, nameRule) \
364
case roles::geckoRole: \
365
return androidClass;
366
367
switch (aRole) {
368
#include "RoleMap.h"
369
default:
370
return java::SessionAccessibility::CLASSNAME_VIEW;
371
}
372
373
#undef ROLE
374
}
375
376
int32_t AccessibleWrap::GetInputType(const nsString& aInputTypeAttr) {
377
if (aInputTypeAttr.EqualsIgnoreCase("email")) {
378
return java::sdk::InputType::TYPE_CLASS_TEXT |
379
java::sdk::InputType::TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
380
}
381
382
if (aInputTypeAttr.EqualsIgnoreCase("number")) {
383
return java::sdk::InputType::TYPE_CLASS_NUMBER;
384
}
385
386
if (aInputTypeAttr.EqualsIgnoreCase("password")) {
387
return java::sdk::InputType::TYPE_CLASS_TEXT |
388
java::sdk::InputType::TYPE_TEXT_VARIATION_WEB_PASSWORD;
389
}
390
391
if (aInputTypeAttr.EqualsIgnoreCase("tel")) {
392
return java::sdk::InputType::TYPE_CLASS_PHONE;
393
}
394
395
if (aInputTypeAttr.EqualsIgnoreCase("text")) {
396
return java::sdk::InputType::TYPE_CLASS_TEXT |
397
java::sdk::InputType::TYPE_TEXT_VARIATION_WEB_EDIT_TEXT;
398
}
399
400
if (aInputTypeAttr.EqualsIgnoreCase("url")) {
401
return java::sdk::InputType::TYPE_CLASS_TEXT |
402
java::sdk::InputType::TYPE_TEXT_VARIATION_URI;
403
}
404
405
return 0;
406
}
407
408
void AccessibleWrap::WrapperDOMNodeID(nsString& aDOMNodeID) {
409
if (mContent) {
410
nsAtom* id = mContent->GetID();
411
if (id) {
412
id->ToString(aDOMNodeID);
413
}
414
}
415
}
416
417
bool AccessibleWrap::WrapperRangeInfo(double* aCurVal, double* aMinVal,
418
double* aMaxVal, double* aStep) {
419
if (HasNumericValue()) {
420
*aCurVal = CurValue();
421
*aMinVal = MinValue();
422
*aMaxVal = MaxValue();
423
*aStep = Step();
424
return true;
425
}
426
427
return false;
428
}
429
430
mozilla::java::GeckoBundle::LocalRef AccessibleWrap::ToBundle(bool aSmall) {
431
nsAutoString name;
432
Name(name);
433
nsAutoString textValue;
434
Value(textValue);
435
nsAutoString nodeID;
436
WrapperDOMNodeID(nodeID);
437
nsAutoString description;
438
Description(description);
439
440
if (aSmall) {
441
return ToBundle(State(), Bounds(), ActionCount(), name, textValue, nodeID,
442
description);
443
}
444
445
double curValue = UnspecifiedNaN<double>();
446
double minValue = UnspecifiedNaN<double>();
447
double maxValue = UnspecifiedNaN<double>();
448
double step = UnspecifiedNaN<double>();
449
WrapperRangeInfo(&curValue, &minValue, &maxValue, &step);
450
451
nsCOMPtr<nsIPersistentProperties> attributes = Attributes();
452
453
return ToBundle(State(), Bounds(), ActionCount(), name, textValue, nodeID,
454
description, curValue, minValue, maxValue, step, attributes);
455
}
456
457
mozilla::java::GeckoBundle::LocalRef AccessibleWrap::ToBundle(
458
const uint64_t aState, const nsIntRect& aBounds, const uint8_t aActionCount,
459
const nsString& aName, const nsString& aTextValue,
460
const nsString& aDOMNodeID, const nsString& aDescription,
461
const double& aCurVal, const double& aMinVal, const double& aMaxVal,
462
const double& aStep, nsIPersistentProperties* aAttributes) {
463
if (!IsProxy() && IsDefunct()) {
464
return nullptr;
465
}
466
467
GECKOBUNDLE_START(nodeInfo);
468
GECKOBUNDLE_PUT(nodeInfo, "id", java::sdk::Integer::ValueOf(VirtualViewID()));
469
470
AccessibleWrap* parent = WrapperParent();
471
GECKOBUNDLE_PUT(
472
nodeInfo, "parentId",
473
java::sdk::Integer::ValueOf(parent ? parent->VirtualViewID() : 0));
474
475
role role = WrapperRole();
476
uint32_t flags = GetFlags(role, aState, aActionCount);
477
GECKOBUNDLE_PUT(nodeInfo, "flags", java::sdk::Integer::ValueOf(flags));
478
GECKOBUNDLE_PUT(nodeInfo, "className",
479
java::sdk::Integer::ValueOf(AndroidClass()));
480
481
if (aState & states::EDITABLE) {
482
nsAutoString hint(aName);
483
if (!aDescription.IsEmpty()) {
484
hint.AppendLiteral(" ");
485
hint.Append(aDescription);
486
}
487
GECKOBUNDLE_PUT(nodeInfo, "hint", jni::StringParam(hint));
488
GECKOBUNDLE_PUT(nodeInfo, "text", jni::StringParam(aTextValue));
489
} else {
490
GECKOBUNDLE_PUT(nodeInfo, "text", jni::StringParam(aName));
491
if (!aDescription.IsEmpty()) {
492
GECKOBUNDLE_PUT(nodeInfo, "hint", jni::StringParam(aDescription));
493
}
494
}
495
496
nsAutoString geckoRole;
497
nsAutoString roleDescription;
498
if (VirtualViewID() != kNoID) {
499
GetRoleDescription(role, aAttributes, geckoRole, roleDescription);
500
}
501
502
GECKOBUNDLE_PUT(nodeInfo, "roleDescription",
503
jni::StringParam(roleDescription));
504
GECKOBUNDLE_PUT(nodeInfo, "geckoRole", jni::StringParam(geckoRole));
505
506
GECKOBUNDLE_PUT(nodeInfo, "roleDescription",
507
jni::StringParam(roleDescription));
508
GECKOBUNDLE_PUT(nodeInfo, "geckoRole", jni::StringParam(geckoRole));
509
510
if (!aDOMNodeID.IsEmpty()) {
511
GECKOBUNDLE_PUT(nodeInfo, "viewIdResourceName",
512
jni::StringParam(aDOMNodeID));
513
}
514
515
nsIntRect bounds = Bounds();
516
const int32_t data[4] = {bounds.x, bounds.y, bounds.x + bounds.width,
517
bounds.y + bounds.height};
518
GECKOBUNDLE_PUT(nodeInfo, "bounds", jni::IntArray::New(data, 4));
519
520
if (HasNumericValue()) {
521
GECKOBUNDLE_START(rangeInfo);
522
if (aMaxVal == 1 && aMinVal == 0) {
523
GECKOBUNDLE_PUT(rangeInfo, "type",
524
java::sdk::Integer::ValueOf(2)); // percent
525
} else if (std::round(aStep) != aStep) {
526
GECKOBUNDLE_PUT(rangeInfo, "type",
527
java::sdk::Integer::ValueOf(1)); // float
528
} else {
529
GECKOBUNDLE_PUT(rangeInfo, "type",
530
java::sdk::Integer::ValueOf(0)); // integer
531
}
532
533
if (!IsNaN(aCurVal)) {
534
GECKOBUNDLE_PUT(rangeInfo, "current", java::sdk::Double::New(aCurVal));
535
}
536
if (!IsNaN(aMinVal)) {
537
GECKOBUNDLE_PUT(rangeInfo, "min", java::sdk::Double::New(aMinVal));
538
}
539
if (!IsNaN(aMaxVal)) {
540
GECKOBUNDLE_PUT(rangeInfo, "max", java::sdk::Double::New(aMaxVal));
541
}
542
543
GECKOBUNDLE_FINISH(rangeInfo);
544
GECKOBUNDLE_PUT(nodeInfo, "rangeInfo", rangeInfo);
545
}
546
547
if (aAttributes) {
548
nsString inputTypeAttr;
549
nsAccUtils::GetAccAttr(aAttributes, nsGkAtoms::textInputType,
550
inputTypeAttr);
551
int32_t inputType = GetInputType(inputTypeAttr);
552
if (inputType) {
553
GECKOBUNDLE_PUT(nodeInfo, "inputType",
554
java::sdk::Integer::ValueOf(inputType));
555
}
556
557
nsString posinset;
558
nsresult rv = aAttributes->GetStringProperty(NS_LITERAL_CSTRING("posinset"),
559
posinset);
560
if (NS_SUCCEEDED(rv)) {
561
int32_t rowIndex;
562
if (sscanf(NS_ConvertUTF16toUTF8(posinset).get(), "%d", &rowIndex) > 0) {
563
GECKOBUNDLE_START(collectionItemInfo);
564
GECKOBUNDLE_PUT(collectionItemInfo, "rowIndex",
565
java::sdk::Integer::ValueOf(rowIndex));
566
GECKOBUNDLE_PUT(collectionItemInfo, "columnIndex",
567
java::sdk::Integer::ValueOf(0));
568
GECKOBUNDLE_PUT(collectionItemInfo, "rowSpan",
569
java::sdk::Integer::ValueOf(1));
570
GECKOBUNDLE_PUT(collectionItemInfo, "columnSpan",
571
java::sdk::Integer::ValueOf(1));
572
GECKOBUNDLE_FINISH(collectionItemInfo);
573
574
GECKOBUNDLE_PUT(nodeInfo, "collectionItemInfo", collectionItemInfo);
575
}
576
}
577
578
nsString colSize;
579
rv = aAttributes->GetStringProperty(NS_LITERAL_CSTRING("child-item-count"),
580
colSize);
581
if (NS_SUCCEEDED(rv)) {
582
int32_t rowCount;
583
if (sscanf(NS_ConvertUTF16toUTF8(colSize).get(), "%d", &rowCount) > 0) {
584
GECKOBUNDLE_START(collectionInfo);
585
GECKOBUNDLE_PUT(collectionInfo, "rowCount",
586
java::sdk::Integer::ValueOf(rowCount));
587
GECKOBUNDLE_PUT(collectionInfo, "columnCount",
588
java::sdk::Integer::ValueOf(1));
589
590
nsString unused;
591
rv = aAttributes->GetStringProperty(NS_LITERAL_CSTRING("hierarchical"),
592
unused);
593
if (NS_SUCCEEDED(rv)) {
594
GECKOBUNDLE_PUT(collectionInfo, "isHierarchical",
595
java::sdk::Boolean::TRUE());
596
}
597
598
if (IsSelect()) {
599
int32_t selectionMode = (aState & states::MULTISELECTABLE) ? 2 : 1;
600
GECKOBUNDLE_PUT(collectionInfo, "selectionMode",
601
java::sdk::Integer::ValueOf(selectionMode));
602
}
603
604
GECKOBUNDLE_FINISH(collectionInfo);
605
GECKOBUNDLE_PUT(nodeInfo, "collectionInfo", collectionInfo);
606
}
607
}
608
}
609
610
bool mustPrune =
611
IsProxy() ? nsAccUtils::MustPrune(Proxy()) : nsAccUtils::MustPrune(this);
612
if (!mustPrune) {
613
auto childCount = ChildCount();
614
nsTArray<int32_t> children(childCount);
615
for (uint32_t i = 0; i < childCount; i++) {
616
auto child = static_cast<AccessibleWrap*>(GetChildAt(i));
617
children.AppendElement(child->VirtualViewID());
618
}
619
620
GECKOBUNDLE_PUT(nodeInfo, "children",
621
jni::IntArray::New(children.Elements(), children.Length()));
622
}
623
624
GECKOBUNDLE_FINISH(nodeInfo);
625
626
return nodeInfo;
627
}
628
629
void AccessibleWrap::GetTextEquiv(nsString& aText) {
630
if (nsTextEquivUtils::HasNameRule(this, eNameFromSubtreeIfReqRule)) {
631
// This is an accessible that normally doesn't get its name from its
632
// subtree, so we collect the text equivalent explicitly.
633
nsTextEquivUtils::GetTextEquivFromSubtree(this, aText);
634
} else {
635
Name(aText);
636
}
637
}
638
639
bool AccessibleWrap::HandleLiveRegionEvent(AccEvent* aEvent) {
640
auto eventType = aEvent->GetEventType();
641
if (eventType != nsIAccessibleEvent::EVENT_TEXT_INSERTED &&
642
eventType != nsIAccessibleEvent::EVENT_NAME_CHANGE) {
643
// XXX: Right now only announce text inserted events. aria-relevant=removals
644
// is potentially on the chopping block[1]. We also don't support editable
645
// text because we currently can't descern the source of the change[2].
648
return false;
649
}
650
651
if (aEvent->IsFromUserInput()) {
652
return false;
653
}
654
655
nsCOMPtr<nsIPersistentProperties> attributes = Attributes();
656
nsString live;
657
nsresult rv =
658
attributes->GetStringProperty(NS_LITERAL_CSTRING("container-live"), live);
659
if (!NS_SUCCEEDED(rv)) {
660
return false;
661
}
662
663
uint16_t priority = live.EqualsIgnoreCase("assertive")
664
? nsIAccessibleAnnouncementEvent::ASSERTIVE
665
: nsIAccessibleAnnouncementEvent::POLITE;
666
667
nsString atomic;
668
rv = attributes->GetStringProperty(NS_LITERAL_CSTRING("container-atomic"),
669
atomic);
670
671
Accessible* announcementTarget = this;
672
nsAutoString announcement;
673
if (atomic.EqualsIgnoreCase("true")) {
674
Accessible* atomicAncestor = nullptr;
675
for (Accessible* parent = announcementTarget; parent;
676
parent = parent->Parent()) {
677
Element* element = parent->Elm();
678
if (element &&
679
element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_atomic,
680
nsGkAtoms::_true, eCaseMatters)) {
681
atomicAncestor = parent;
682
break;
683
}
684
}
685
686
if (atomicAncestor) {
687
announcementTarget = atomicAncestor;
688
static_cast<AccessibleWrap*>(atomicAncestor)->GetTextEquiv(announcement);
689
}
690
} else {
691
GetTextEquiv(announcement);
692
}
693
694
announcement.CompressWhitespace();
695
if (announcement.IsEmpty()) {
696
return false;
697
}
698
699
announcementTarget->Announce(announcement, priority);
700
return true;
701
}