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 "AccessibleWrap.h"
9 #include "LocalAccessible-inl.h"
10 #include "AccAttributes.h"
11 #include "ApplicationAccessibleWrap.h"
12 #include "InterfaceInitFuncs.h"
13 #include "nsAccUtils.h"
14 #include "mozilla/a11y/PDocAccessible.h"
15 #include "OuterDocAccessible.h"
16 #include "RemoteAccessible.h"
17 #include "DocAccessibleParent.h"
18 #include "RootAccessible.h"
19 #include "mozilla/a11y/TableAccessible.h"
20 #include "mozilla/a11y/TableCellAccessible.h"
22 #include "nsMaiHyperlink.h"
24 #include "nsStateMap.h"
25 #include "mozilla/a11y/Platform.h"
27 #include "RootAccessible.h"
29 #include "nsISimpleEnumerator.h"
31 #include "mozilla/ArrayUtils.h"
32 #include "mozilla/Sprintf.h"
33 #include "nsAccessibilityService.h"
34 #include "nsComponentManagerUtils.h"
36 using namespace mozilla
;
37 using namespace mozilla::a11y
;
39 MaiAtkObject::EAvailableAtkSignals
MaiAtkObject::gAvailableAtkSignals
=
42 // defined in ApplicationAccessibleWrap.cpp
43 extern "C" GType g_atk_hyperlink_impl_type
;
59 enum MaiInterfaceType
{
60 MAI_INTERFACE_COMPONENT
, /* 0 */
63 MAI_INTERFACE_EDITABLE_TEXT
,
64 MAI_INTERFACE_HYPERTEXT
,
65 MAI_INTERFACE_HYPERLINK_IMPL
,
66 MAI_INTERFACE_SELECTION
,
69 MAI_INTERFACE_DOCUMENT
,
70 MAI_INTERFACE_IMAGE
, /* 10 */
71 MAI_INTERFACE_TABLE_CELL
74 static GType
GetAtkTypeForMai(MaiInterfaceType type
) {
76 case MAI_INTERFACE_COMPONENT
:
77 return ATK_TYPE_COMPONENT
;
78 case MAI_INTERFACE_ACTION
:
79 return ATK_TYPE_ACTION
;
80 case MAI_INTERFACE_VALUE
:
81 return ATK_TYPE_VALUE
;
82 case MAI_INTERFACE_EDITABLE_TEXT
:
83 return ATK_TYPE_EDITABLE_TEXT
;
84 case MAI_INTERFACE_HYPERTEXT
:
85 return ATK_TYPE_HYPERTEXT
;
86 case MAI_INTERFACE_HYPERLINK_IMPL
:
87 return g_atk_hyperlink_impl_type
;
88 case MAI_INTERFACE_SELECTION
:
89 return ATK_TYPE_SELECTION
;
90 case MAI_INTERFACE_TABLE
:
91 return ATK_TYPE_TABLE
;
92 case MAI_INTERFACE_TEXT
:
94 case MAI_INTERFACE_DOCUMENT
:
95 return ATK_TYPE_DOCUMENT
;
96 case MAI_INTERFACE_IMAGE
:
97 return ATK_TYPE_IMAGE
;
98 case MAI_INTERFACE_TABLE_CELL
:
101 return G_TYPE_INVALID
;
104 #define NON_USER_EVENT ":system"
106 // The atk interfaces we can expose without checking what version of ATK we are
107 // dealing with. At the moment AtkTableCell is the only interface we can't
109 static const GInterfaceInfo atk_if_infos
[] = {
110 {(GInterfaceInitFunc
)componentInterfaceInitCB
,
111 (GInterfaceFinalizeFunc
) nullptr, nullptr},
112 {(GInterfaceInitFunc
)actionInterfaceInitCB
,
113 (GInterfaceFinalizeFunc
) nullptr, nullptr},
114 {(GInterfaceInitFunc
)valueInterfaceInitCB
, (GInterfaceFinalizeFunc
) nullptr,
116 {(GInterfaceInitFunc
)editableTextInterfaceInitCB
,
117 (GInterfaceFinalizeFunc
) nullptr, nullptr},
118 {(GInterfaceInitFunc
)hypertextInterfaceInitCB
,
119 (GInterfaceFinalizeFunc
) nullptr, nullptr},
120 {(GInterfaceInitFunc
)hyperlinkImplInterfaceInitCB
,
121 (GInterfaceFinalizeFunc
) nullptr, nullptr},
122 {(GInterfaceInitFunc
)selectionInterfaceInitCB
,
123 (GInterfaceFinalizeFunc
) nullptr, nullptr},
124 {(GInterfaceInitFunc
)tableInterfaceInitCB
, (GInterfaceFinalizeFunc
) nullptr,
126 {(GInterfaceInitFunc
)textInterfaceInitCB
, (GInterfaceFinalizeFunc
) nullptr,
128 {(GInterfaceInitFunc
)documentInterfaceInitCB
,
129 (GInterfaceFinalizeFunc
) nullptr, nullptr},
130 {(GInterfaceInitFunc
)imageInterfaceInitCB
, (GInterfaceFinalizeFunc
) nullptr,
133 static GQuark quark_mai_hyperlink
= 0;
135 AtkHyperlink
* MaiAtkObject::GetAtkHyperlink() {
136 NS_ASSERTION(quark_mai_hyperlink
, "quark_mai_hyperlink not initialized");
137 MaiHyperlink
* maiHyperlink
=
138 (MaiHyperlink
*)g_object_get_qdata(G_OBJECT(this), quark_mai_hyperlink
);
140 maiHyperlink
= new MaiHyperlink(acc
);
141 g_object_set_qdata(G_OBJECT(this), quark_mai_hyperlink
, maiHyperlink
);
144 return maiHyperlink
->GetAtkHyperlink();
147 void MaiAtkObject::Shutdown() {
149 MaiHyperlink
* maiHyperlink
=
150 (MaiHyperlink
*)g_object_get_qdata(G_OBJECT(this), quark_mai_hyperlink
);
153 g_object_set_qdata(G_OBJECT(this), quark_mai_hyperlink
, nullptr);
157 struct MaiAtkObjectClass
{
158 AtkObjectClass parent_class
;
161 static guint mai_atk_object_signals
[LAST_SIGNAL
] = {
165 static void MaybeFireNameChange(AtkObject
* aAtkObj
, const nsString
& aNewName
);
168 /* callbacks for MaiAtkObject */
169 static void classInitCB(AtkObjectClass
* aClass
);
170 static void initializeCB(AtkObject
* aAtkObj
, gpointer aData
);
171 static void finalizeCB(GObject
* aObj
);
173 /* callbacks for AtkObject virtual functions */
174 static const gchar
* getNameCB(AtkObject
* aAtkObj
);
175 /* getDescriptionCB is also used by image interface */
176 const gchar
* getDescriptionCB(AtkObject
* aAtkObj
);
177 static AtkRole
getRoleCB(AtkObject
* aAtkObj
);
178 static AtkAttributeSet
* getAttributesCB(AtkObject
* aAtkObj
);
179 static const gchar
* GetLocaleCB(AtkObject
*);
180 static AtkObject
* getParentCB(AtkObject
* aAtkObj
);
181 static gint
getChildCountCB(AtkObject
* aAtkObj
);
182 static AtkObject
* refChildCB(AtkObject
* aAtkObj
, gint aChildIndex
);
183 static gint
getIndexInParentCB(AtkObject
* aAtkObj
);
184 static AtkStateSet
* refStateSetCB(AtkObject
* aAtkObj
);
185 static AtkRelationSet
* refRelationSetCB(AtkObject
* aAtkObj
);
187 /* the missing atkobject virtual functions */
189 static AtkLayer getLayerCB(AtkObject *aAtkObj);
190 static gint getMdiZorderCB(AtkObject *aAtkObj);
191 static void SetNameCB(AtkObject *aAtkObj,
193 static void SetDescriptionCB(AtkObject *aAtkObj,
194 const gchar *description);
195 static void SetParentCB(AtkObject *aAtkObj,
197 static void SetRoleCB(AtkObject *aAtkObj,
199 static guint ConnectPropertyChangeHandlerCB(
201 AtkPropertyChangeHandler *handler);
202 static void RemovePropertyChangeHandlerCB(
205 static void InitializeCB(AtkObject *aAtkObj,
207 static void ChildrenChangedCB(AtkObject *aAtkObj,
209 gpointer changed_child);
210 static void FocusEventCB(AtkObject *aAtkObj,
212 static void PropertyChangeCB(AtkObject *aAtkObj,
213 AtkPropertyValues *values);
214 static void StateChangeCB(AtkObject *aAtkObj,
217 static void VisibleDataChangedCB(AtkObject *aAtkObj);
221 static GType
GetMaiAtkType(uint16_t interfacesBits
);
222 static const char* GetUniqueMaiAtkTypeName(uint16_t interfacesBits
);
224 static gpointer parent_class
= nullptr;
226 GType
mai_atk_object_get_type(void) {
227 static GType type
= 0;
230 static const GTypeInfo tinfo
= {
231 sizeof(MaiAtkObjectClass
),
232 (GBaseInitFunc
) nullptr,
233 (GBaseFinalizeFunc
) nullptr,
234 (GClassInitFunc
)classInitCB
,
235 (GClassFinalizeFunc
) nullptr,
236 nullptr, /* class data */
237 sizeof(MaiAtkObject
), /* instance size */
238 0, /* nb preallocs */
239 (GInstanceInitFunc
) nullptr,
240 nullptr /* value table */
243 type
= g_type_register_static(ATK_TYPE_OBJECT
, "MaiAtkObject", &tinfo
,
245 quark_mai_hyperlink
= g_quark_from_static_string("MaiHyperlink");
250 AccessibleWrap::AccessibleWrap(nsIContent
* aContent
, DocAccessible
* aDoc
)
251 : LocalAccessible(aContent
, aDoc
), mAtkObject(nullptr) {}
253 AccessibleWrap::~AccessibleWrap() {
254 NS_ASSERTION(!mAtkObject
, "ShutdownAtkObject() is not called");
257 void AccessibleWrap::ShutdownAtkObject() {
258 if (!mAtkObject
) return;
260 NS_ASSERTION(IS_MAI_OBJECT(mAtkObject
), "wrong type of atk object");
261 if (IS_MAI_OBJECT(mAtkObject
)) MAI_ATK_OBJECT(mAtkObject
)->Shutdown();
263 g_object_unref(mAtkObject
);
264 mAtkObject
= nullptr;
267 void AccessibleWrap::Shutdown() {
269 LocalAccessible::Shutdown();
272 void AccessibleWrap::GetNativeInterface(void** aOutAccessible
) {
273 *aOutAccessible
= nullptr;
276 if (IsDefunct() || IsText()) {
277 // We don't create ATK objects for node which has been shutdown or
282 GType type
= GetMaiAtkType(CreateMaiInterfaces());
285 mAtkObject
= reinterpret_cast<AtkObject
*>(g_object_new(type
, nullptr));
286 if (!mAtkObject
) return;
288 atk_object_initialize(mAtkObject
, static_cast<Accessible
*>(this));
289 mAtkObject
->role
= ATK_ROLE_INVALID
;
290 mAtkObject
->layer
= ATK_LAYER_INVALID
;
293 *aOutAccessible
= mAtkObject
;
296 AtkObject
* AccessibleWrap::GetAtkObject(void) {
297 void* atkObj
= nullptr;
298 GetNativeInterface(&atkObj
);
299 return static_cast<AtkObject
*>(atkObj
);
302 // Get AtkObject from LocalAccessible interface
304 AtkObject
* AccessibleWrap::GetAtkObject(LocalAccessible
* acc
) {
305 void* atkObjPtr
= nullptr;
306 acc
->GetNativeInterface(&atkObjPtr
);
307 return atkObjPtr
? ATK_OBJECT(atkObjPtr
) : nullptr;
311 uint16_t AccessibleWrap::CreateMaiInterfaces(void) {
312 uint16_t interfacesBits
= 0;
314 // The Component interface is supported by all accessibles.
315 interfacesBits
|= 1 << MAI_INTERFACE_COMPONENT
;
317 // Add Action interface if the action count is more than zero.
318 if (ActionCount() > 0) interfacesBits
|= 1 << MAI_INTERFACE_ACTION
;
320 // Text, Editabletext, and Hypertext interface.
321 HyperTextAccessible
* hyperText
= AsHyperText();
322 if (hyperText
&& hyperText
->IsTextRole()) {
323 interfacesBits
|= 1 << MAI_INTERFACE_TEXT
;
324 interfacesBits
|= 1 << MAI_INTERFACE_EDITABLE_TEXT
;
325 if (!nsAccUtils::MustPrune(this)) {
326 interfacesBits
|= 1 << MAI_INTERFACE_HYPERTEXT
;
331 if (HasNumericValue()) interfacesBits
|= 1 << MAI_INTERFACE_VALUE
;
333 // Document interface.
334 if (IsDoc()) interfacesBits
|= 1 << MAI_INTERFACE_DOCUMENT
;
336 if (IsImage()) interfacesBits
|= 1 << MAI_INTERFACE_IMAGE
;
338 // HyperLink interface.
339 if (IsLink()) interfacesBits
|= 1 << MAI_INTERFACE_HYPERLINK_IMPL
;
341 if (!nsAccUtils::MustPrune(this)) { // These interfaces require children
343 if (AsTable()) interfacesBits
|= 1 << MAI_INTERFACE_TABLE
;
345 if (AsTableCell()) interfacesBits
|= 1 << MAI_INTERFACE_TABLE_CELL
;
347 // Selection interface.
349 interfacesBits
|= 1 << MAI_INTERFACE_SELECTION
;
353 return interfacesBits
;
356 static GType
GetMaiAtkType(uint16_t interfacesBits
) {
358 static const GTypeInfo tinfo
= {
359 sizeof(MaiAtkObjectClass
),
360 (GBaseInitFunc
) nullptr,
361 (GBaseFinalizeFunc
) nullptr,
362 (GClassInitFunc
) nullptr,
363 (GClassFinalizeFunc
) nullptr,
364 nullptr, /* class data */
365 sizeof(MaiAtkObject
), /* instance size */
366 0, /* nb preallocs */
367 (GInstanceInitFunc
) nullptr,
368 nullptr /* value table */
372 * The members we use to register GTypes are GetAtkTypeForMai
373 * and atk_if_infos, which are constant values to each MaiInterface
374 * So we can reuse the registered GType when having
375 * the same MaiInterface types.
377 const char* atkTypeName
= GetUniqueMaiAtkTypeName(interfacesBits
);
378 type
= g_type_from_name(atkTypeName
);
384 * gobject limits the number of types that can directly derive from any
385 * given object type to 4095.
387 static uint16_t typeRegCount
= 0;
388 if (typeRegCount
++ >= 4095) {
389 return G_TYPE_INVALID
;
391 type
= g_type_register_static(MAI_TYPE_ATK_OBJECT
, atkTypeName
, &tinfo
,
394 for (uint32_t index
= 0; index
< ArrayLength(atk_if_infos
); index
++) {
395 if (interfacesBits
& (1 << index
)) {
396 g_type_add_interface_static(type
,
397 GetAtkTypeForMai((MaiInterfaceType
)index
),
398 &atk_if_infos
[index
]);
402 // Special case AtkTableCell so we can check what version of Atk we are
404 if (IsAtkVersionAtLeast(2, 12) &&
405 (interfacesBits
& (1 << MAI_INTERFACE_TABLE_CELL
))) {
406 const GInterfaceInfo cellInfo
= {
407 (GInterfaceInitFunc
)tableCellInterfaceInitCB
,
408 (GInterfaceFinalizeFunc
) nullptr, nullptr};
409 g_type_add_interface_static(type
, gAtkTableCellGetTypeFunc(), &cellInfo
);
415 static const char* GetUniqueMaiAtkTypeName(uint16_t interfacesBits
) {
416 #define MAI_ATK_TYPE_NAME_LEN (30) /* 10+sizeof(uint16_t)*8/4+1 < 30 */
418 static gchar namePrefix
[] = "MaiAtkType"; /* size = 10 */
419 static gchar name
[MAI_ATK_TYPE_NAME_LEN
+ 1];
421 SprintfLiteral(name
, "%s%x", namePrefix
, interfacesBits
);
422 name
[MAI_ATK_TYPE_NAME_LEN
] = '\0';
427 bool AccessibleWrap::IsValidObject() {
428 // to ensure we are not shut down
432 /* static functions for ATK callbacks */
433 void classInitCB(AtkObjectClass
* aClass
) {
434 GObjectClass
* gobject_class
= G_OBJECT_CLASS(aClass
);
436 parent_class
= g_type_class_peek_parent(aClass
);
438 aClass
->get_name
= getNameCB
;
439 aClass
->get_description
= getDescriptionCB
;
440 aClass
->get_parent
= getParentCB
;
441 aClass
->get_n_children
= getChildCountCB
;
442 aClass
->ref_child
= refChildCB
;
443 aClass
->get_index_in_parent
= getIndexInParentCB
;
444 aClass
->get_role
= getRoleCB
;
445 aClass
->get_attributes
= getAttributesCB
;
446 aClass
->get_object_locale
= GetLocaleCB
;
447 aClass
->ref_state_set
= refStateSetCB
;
448 aClass
->ref_relation_set
= refRelationSetCB
;
450 aClass
->initialize
= initializeCB
;
452 gobject_class
->finalize
= finalizeCB
;
454 mai_atk_object_signals
[ACTIVATE
] = g_signal_new(
455 "activate", MAI_TYPE_ATK_OBJECT
, G_SIGNAL_RUN_LAST
,
456 0, /* default signal handler */
457 nullptr, nullptr, g_cclosure_marshal_VOID__VOID
, G_TYPE_NONE
, 0);
458 mai_atk_object_signals
[CREATE
] = g_signal_new(
459 "create", MAI_TYPE_ATK_OBJECT
, G_SIGNAL_RUN_LAST
,
460 0, /* default signal handler */
461 nullptr, nullptr, g_cclosure_marshal_VOID__VOID
, G_TYPE_NONE
, 0);
462 mai_atk_object_signals
[DEACTIVATE
] = g_signal_new(
463 "deactivate", MAI_TYPE_ATK_OBJECT
, G_SIGNAL_RUN_LAST
,
464 0, /* default signal handler */
465 nullptr, nullptr, g_cclosure_marshal_VOID__VOID
, G_TYPE_NONE
, 0);
466 mai_atk_object_signals
[DESTROY
] = g_signal_new(
467 "destroy", MAI_TYPE_ATK_OBJECT
, G_SIGNAL_RUN_LAST
,
468 0, /* default signal handler */
469 nullptr, nullptr, g_cclosure_marshal_VOID__VOID
, G_TYPE_NONE
, 0);
470 mai_atk_object_signals
[MAXIMIZE
] = g_signal_new(
471 "maximize", MAI_TYPE_ATK_OBJECT
, G_SIGNAL_RUN_LAST
,
472 0, /* default signal handler */
473 nullptr, nullptr, g_cclosure_marshal_VOID__VOID
, G_TYPE_NONE
, 0);
474 mai_atk_object_signals
[MINIMIZE
] = g_signal_new(
475 "minimize", MAI_TYPE_ATK_OBJECT
, G_SIGNAL_RUN_LAST
,
476 0, /* default signal handler */
477 nullptr, nullptr, g_cclosure_marshal_VOID__VOID
, G_TYPE_NONE
, 0);
478 mai_atk_object_signals
[RESIZE
] = g_signal_new(
479 "resize", MAI_TYPE_ATK_OBJECT
, G_SIGNAL_RUN_LAST
,
480 0, /* default signal handler */
481 nullptr, nullptr, g_cclosure_marshal_VOID__VOID
, G_TYPE_NONE
, 0);
482 mai_atk_object_signals
[RESTORE
] = g_signal_new(
483 "restore", MAI_TYPE_ATK_OBJECT
, G_SIGNAL_RUN_LAST
,
484 0, /* default signal handler */
485 nullptr, nullptr, g_cclosure_marshal_VOID__VOID
, G_TYPE_NONE
, 0);
488 void initializeCB(AtkObject
* aAtkObj
, gpointer aData
) {
489 NS_ASSERTION((IS_MAI_OBJECT(aAtkObj
)), "Invalid AtkObject");
490 NS_ASSERTION(aData
, "Invalid Data to init AtkObject");
491 if (!aAtkObj
|| !aData
) return;
493 /* call parent init function */
494 /* AtkObjectClass has not a "initialize" function now,
498 if (ATK_OBJECT_CLASS(parent_class
)->initialize
) {
499 ATK_OBJECT_CLASS(parent_class
)->initialize(aAtkObj
, aData
);
502 /* initialize object */
503 MAI_ATK_OBJECT(aAtkObj
)->acc
= static_cast<Accessible
*>(aData
);
506 void finalizeCB(GObject
* aObj
) {
507 if (!IS_MAI_OBJECT(aObj
)) return;
508 NS_ASSERTION(!MAI_ATK_OBJECT(aObj
)->acc
, "acc NOT null");
510 // call parent finalize function
511 // finalize of GObjectClass will unref the accessible parent if has
512 if (G_OBJECT_CLASS(parent_class
)->finalize
) {
513 G_OBJECT_CLASS(parent_class
)->finalize(aObj
);
517 const gchar
* getNameCB(AtkObject
* aAtkObj
) {
519 if (Accessible
* acc
= GetInternalObj(aAtkObj
)) {
525 // XXX Firing an event from here does not seem right
526 MaybeFireNameChange(aAtkObj
, name
);
528 return aAtkObj
->name
;
531 static void MaybeFireNameChange(AtkObject
* aAtkObj
, const nsString
& aNewName
) {
532 NS_ConvertUTF16toUTF8
newNameUTF8(aNewName
);
533 if (aAtkObj
->name
&& !strcmp(aAtkObj
->name
, newNameUTF8
.get())) return;
535 // Below we duplicate the functionality of atk_object_set_name(),
536 // but without calling atk_object_get_name(). Instead of
537 // atk_object_get_name() we directly access aAtkObj->name. This is because
538 // atk_object_get_name() would call getNameCB() which would call
539 // MaybeFireNameChange() (or atk_object_set_name() before this problem was
540 // fixed) and we would get an infinite recursion.
541 // See http://bugzilla.mozilla.org/733712
543 // Do not notify for initial name setting.
544 // See bug http://bugzilla.gnome.org/665870
545 bool notify
= !!aAtkObj
->name
;
548 aAtkObj
->name
= strdup(newNameUTF8
.get());
550 if (notify
) g_object_notify(G_OBJECT(aAtkObj
), "accessible-name");
553 const gchar
* getDescriptionCB(AtkObject
* aAtkObj
) {
554 nsAutoString uniDesc
;
555 if (Accessible
* acc
= GetInternalObj(aAtkObj
)) {
556 acc
->Description(uniDesc
);
561 NS_ConvertUTF8toUTF16
objDesc(aAtkObj
->description
);
562 if (!uniDesc
.Equals(objDesc
)) {
563 atk_object_set_description(aAtkObj
, NS_ConvertUTF16toUTF8(uniDesc
).get());
566 return aAtkObj
->description
;
569 AtkRole
getRoleCB(AtkObject
* aAtkObj
) {
570 if (aAtkObj
->role
!= ATK_ROLE_INVALID
) return aAtkObj
->role
;
572 Accessible
* acc
= GetInternalObj(aAtkObj
);
574 return ATK_ROLE_INVALID
;
578 if (AccessibleWrap
* accWrap
= GetAccessibleWrap(aAtkObj
)) {
579 NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(accWrap
),
580 "Does not support Text interface when it should");
584 #define ROLE(geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \
585 msaaRole, ia2Role, androidClass, iosIsElement, uiaControlType, \
587 case roles::geckoRole: \
588 aAtkObj->role = atkRole; \
591 switch (acc
->Role()) {
594 MOZ_CRASH("Unknown role.");
599 if (aAtkObj
->role
== ATK_ROLE_LIST_BOX
&& !IsAtkVersionAtLeast(2, 1)) {
600 aAtkObj
->role
= ATK_ROLE_LIST
;
601 } else if (aAtkObj
->role
== ATK_ROLE_TABLE_ROW
&&
602 !IsAtkVersionAtLeast(2, 1)) {
603 aAtkObj
->role
= ATK_ROLE_LIST_ITEM
;
604 } else if (aAtkObj
->role
== ATK_ROLE_MATH
&& !IsAtkVersionAtLeast(2, 12)) {
605 aAtkObj
->role
= ATK_ROLE_SECTION
;
606 } else if (aAtkObj
->role
== ATK_ROLE_COMMENT
&& !IsAtkVersionAtLeast(2, 12)) {
607 aAtkObj
->role
= ATK_ROLE_SECTION
;
608 } else if (aAtkObj
->role
== ATK_ROLE_LANDMARK
&&
609 !IsAtkVersionAtLeast(2, 12)) {
610 aAtkObj
->role
= ATK_ROLE_SECTION
;
611 } else if (aAtkObj
->role
== ATK_ROLE_FOOTNOTE
&&
612 !IsAtkVersionAtLeast(2, 25, 2)) {
613 aAtkObj
->role
= ATK_ROLE_SECTION
;
614 } else if (aAtkObj
->role
== ATK_ROLE_STATIC
&& !IsAtkVersionAtLeast(2, 16)) {
615 aAtkObj
->role
= ATK_ROLE_TEXT
;
616 } else if ((aAtkObj
->role
== ATK_ROLE_MATH_FRACTION
||
617 aAtkObj
->role
== ATK_ROLE_MATH_ROOT
) &&
618 !IsAtkVersionAtLeast(2, 16)) {
619 aAtkObj
->role
= ATK_ROLE_SECTION
;
620 } else if (aAtkObj
->role
== ATK_ROLE_MARK
&& !IsAtkVersionAtLeast(2, 36)) {
621 aAtkObj
->role
= ATK_ROLE_TEXT
;
622 } else if (aAtkObj
->role
== ATK_ROLE_SUGGESTION
&&
623 !IsAtkVersionAtLeast(2, 36)) {
624 aAtkObj
->role
= ATK_ROLE_SECTION
;
625 } else if (aAtkObj
->role
== ATK_ROLE_COMMENT
&& !IsAtkVersionAtLeast(2, 36)) {
626 aAtkObj
->role
= ATK_ROLE_SECTION
;
627 } else if ((aAtkObj
->role
== ATK_ROLE_CONTENT_DELETION
||
628 aAtkObj
->role
== ATK_ROLE_CONTENT_INSERTION
) &&
629 !IsAtkVersionAtLeast(2, 34)) {
630 aAtkObj
->role
= ATK_ROLE_SECTION
;
633 return aAtkObj
->role
;
636 static AtkAttributeSet
* ConvertToAtkAttributeSet(AccAttributes
* aAttributes
) {
641 AtkAttributeSet
* objAttributeSet
= nullptr;
643 for (auto iter
: *aAttributes
) {
645 iter
.NameAsString(name
);
646 if (name
.Equals(u
"placeholder")) {
647 name
.AssignLiteral(u
"placeholder-text");
651 iter
.ValueAsString(value
);
653 AtkAttribute
* objAttr
= (AtkAttribute
*)g_malloc(sizeof(AtkAttribute
));
654 objAttr
->name
= g_strdup(NS_ConvertUTF16toUTF8(name
).get());
655 objAttr
->value
= g_strdup(NS_ConvertUTF16toUTF8(value
).get());
656 objAttributeSet
= g_slist_prepend(objAttributeSet
, objAttr
);
659 // libspi will free it
660 return objAttributeSet
;
663 AtkAttributeSet
* getAttributesCB(AtkObject
* aAtkObj
) {
664 Accessible
* acc
= GetInternalObj(aAtkObj
);
668 RefPtr
<AccAttributes
> attributes
= acc
->Attributes();
669 return ConvertToAtkAttributeSet(attributes
);
672 const gchar
* GetLocaleCB(AtkObject
* aAtkObj
) {
673 Accessible
* acc
= GetInternalObj(aAtkObj
);
679 acc
->Language(locale
);
680 return AccessibleWrap::ReturnString(locale
);
683 AtkObject
* getParentCB(AtkObject
* aAtkObj
) {
684 if (aAtkObj
->accessible_parent
) return aAtkObj
->accessible_parent
;
686 Accessible
* acc
= GetInternalObj(aAtkObj
);
691 Accessible
* parent
= acc
->Parent();
692 AtkObject
* atkParent
= parent
? GetWrapperFor(parent
) : nullptr;
693 if (atkParent
) atk_object_set_parent(aAtkObj
, atkParent
);
695 return aAtkObj
->accessible_parent
;
698 gint
getChildCountCB(AtkObject
* aAtkObj
) {
699 Accessible
* acc
= GetInternalObj(aAtkObj
);
700 if (!acc
|| nsAccUtils::MustPrune(acc
)) {
703 return static_cast<gint
>(acc
->EmbeddedChildCount());
706 AtkObject
* refChildCB(AtkObject
* aAtkObj
, gint aChildIndex
) {
707 // aChildIndex should not be less than zero
708 if (aChildIndex
< 0) {
712 Accessible
* acc
= GetInternalObj(aAtkObj
);
713 if (!acc
|| nsAccUtils::MustPrune(acc
)) {
716 Accessible
* accChild
= acc
->EmbeddedChildAt(aChildIndex
);
721 AtkObject
* childAtkObj
= GetWrapperFor(accChild
);
722 NS_ASSERTION(childAtkObj
, "Fail to get AtkObj");
727 g_object_ref(childAtkObj
);
729 if (aAtkObj
!= childAtkObj
->accessible_parent
) {
730 atk_object_set_parent(childAtkObj
, aAtkObj
);
736 gint
getIndexInParentCB(AtkObject
* aAtkObj
) {
737 // We don't use LocalAccessible::IndexInParent() because we don't include text
738 // leaf nodes as children in ATK.
739 Accessible
* acc
= GetInternalObj(aAtkObj
);
746 Accessible
* parent
= acc
->Parent();
750 return parent
->IndexOfEmbeddedChild(acc
);
753 static void TranslateStates(uint64_t aState
, roles::Role aRole
,
754 AtkStateSet
* aStateSet
) {
755 // atk doesn't have a read only state so read only things shouldn't be
756 // editable. However, we don't do this for list items because Gecko always
757 // exposes those as read only.
758 if ((aState
& states::READONLY
) && aRole
!= roles::LISTITEM
) {
759 aState
&= ~states::EDITABLE
;
762 // Convert every state to an entry in AtkStateMap
763 uint64_t bitMask
= 1;
764 for (auto stateIndex
= 0U; stateIndex
< gAtkStateMapLen
; stateIndex
++) {
765 if (gAtkStateMap
[stateIndex
]
766 .atkState
) { // There's potentially an ATK state for this
767 bool isStateOn
= (aState
& bitMask
) != 0;
768 if (gAtkStateMap
[stateIndex
].stateMapEntryType
== kMapOpposite
) {
769 isStateOn
= !isStateOn
;
772 atk_state_set_add_state(aStateSet
, gAtkStateMap
[stateIndex
].atkState
);
779 AtkStateSet
* refStateSetCB(AtkObject
* aAtkObj
) {
780 AtkStateSet
* state_set
= nullptr;
781 state_set
= ATK_OBJECT_CLASS(parent_class
)->ref_state_set(aAtkObj
);
783 if (Accessible
* acc
= GetInternalObj(aAtkObj
)) {
784 TranslateStates(acc
->State(), acc
->Role(), state_set
);
786 TranslateStates(states::DEFUNCT
, roles::NOTHING
, state_set
);
792 static void UpdateAtkRelation(RelationType aType
, Accessible
* aAcc
,
793 AtkRelationType aAtkType
,
794 AtkRelationSet
* aAtkSet
) {
795 if (aAtkType
== ATK_RELATION_NULL
) return;
797 AtkRelation
* atkRelation
=
798 atk_relation_set_get_relation_by_type(aAtkSet
, aAtkType
);
799 if (atkRelation
) atk_relation_set_remove(aAtkSet
, atkRelation
);
801 Relation
rel(aAcc
->RelationByType(aType
));
802 nsTArray
<AtkObject
*> targets
;
803 Accessible
* tempAcc
= nullptr;
804 while ((tempAcc
= rel
.Next())) {
805 targets
.AppendElement(GetWrapperFor(tempAcc
));
808 if (targets
.Length()) {
810 atk_relation_new(targets
.Elements(), targets
.Length(), aAtkType
);
811 atk_relation_set_add(aAtkSet
, atkRelation
);
812 g_object_unref(atkRelation
);
816 AtkRelationSet
* refRelationSetCB(AtkObject
* aAtkObj
) {
817 AtkRelationSet
* relation_set
=
818 ATK_OBJECT_CLASS(parent_class
)->ref_relation_set(aAtkObj
);
820 Accessible
* acc
= GetInternalObj(aAtkObj
);
825 #define RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) \
826 UpdateAtkRelation(RelationType::geckoType, acc, atkType, relation_set);
828 #include "RelationTypeMap.h"
835 // Check if aAtkObj is a valid MaiAtkObject, and return the AccessibleWrap
837 AccessibleWrap
* GetAccessibleWrap(AtkObject
* aAtkObj
) {
838 NS_ENSURE_TRUE(IS_MAI_OBJECT(aAtkObj
), nullptr);
840 // If we're working with an ATK object, we need to convert the Accessible
841 // back to an AccessibleWrap:
842 Accessible
* storedAcc
= MAI_ATK_OBJECT(aAtkObj
)->acc
;
846 auto* accWrap
= static_cast<AccessibleWrap
*>(storedAcc
->AsLocal());
848 // Check if the accessible was deconstructed.
849 if (!accWrap
) return nullptr;
851 NS_ENSURE_TRUE(accWrap
->GetAtkObject() == aAtkObj
, nullptr);
853 AccessibleWrap
* appAccWrap
= ApplicationAcc();
854 if (appAccWrap
!= accWrap
&& !accWrap
->IsValidObject()) {
861 RemoteAccessible
* GetProxy(AtkObject
* aObj
) {
862 Accessible
* acc
= GetInternalObj(aObj
);
867 return acc
->AsRemote();
870 Accessible
* GetInternalObj(AtkObject
* aObj
) {
871 if (!aObj
|| !IS_MAI_OBJECT(aObj
)) return nullptr;
873 return MAI_ATK_OBJECT(aObj
)->acc
;
876 AtkObject
* GetWrapperFor(Accessible
* aAcc
) {
881 if (aAcc
->IsRemote()) {
882 return reinterpret_cast<AtkObject
*>(aAcc
->AsRemote()->GetWrapper());
885 return AccessibleWrap::GetAtkObject(aAcc
->AsLocal());
888 static uint16_t GetInterfacesForProxy(RemoteAccessible
* aProxy
) {
889 uint16_t interfaces
= 1 << MAI_INTERFACE_COMPONENT
;
890 if (aProxy
->IsHyperText()) {
891 interfaces
|= (1 << MAI_INTERFACE_HYPERTEXT
) | (1 << MAI_INTERFACE_TEXT
) |
892 (1 << MAI_INTERFACE_EDITABLE_TEXT
);
895 if (aProxy
->IsLink()) {
896 interfaces
|= 1 << MAI_INTERFACE_HYPERLINK_IMPL
;
899 if (aProxy
->HasNumericValue()) {
900 interfaces
|= 1 << MAI_INTERFACE_VALUE
;
903 if (aProxy
->IsTable()) {
904 interfaces
|= 1 << MAI_INTERFACE_TABLE
;
907 if (aProxy
->IsTableCell()) {
908 interfaces
|= 1 << MAI_INTERFACE_TABLE_CELL
;
911 if (aProxy
->IsImage()) {
912 interfaces
|= 1 << MAI_INTERFACE_IMAGE
;
915 if (aProxy
->IsDoc()) {
916 interfaces
|= 1 << MAI_INTERFACE_DOCUMENT
;
919 if (aProxy
->IsSelect()) {
920 interfaces
|= 1 << MAI_INTERFACE_SELECTION
;
923 if (aProxy
->IsActionable()) {
924 interfaces
|= 1 << MAI_INTERFACE_ACTION
;
930 void a11y::ProxyCreated(RemoteAccessible
* aProxy
) {
931 MOZ_ASSERT(aProxy
->RemoteParent() || aProxy
->IsDoc(),
932 "Need parent to check for HyperLink interface");
933 GType type
= GetMaiAtkType(GetInterfacesForProxy(aProxy
));
934 NS_ASSERTION(type
, "why don't we have a type!");
936 AtkObject
* obj
= reinterpret_cast<AtkObject
*>(g_object_new(type
, nullptr));
939 atk_object_initialize(obj
, static_cast<Accessible
*>(aProxy
));
940 obj
->role
= ATK_ROLE_INVALID
;
941 obj
->layer
= ATK_LAYER_INVALID
;
942 aProxy
->SetWrapper(reinterpret_cast<uintptr_t>(obj
));
945 void a11y::ProxyDestroyed(RemoteAccessible
* aProxy
) {
946 auto obj
= reinterpret_cast<MaiAtkObject
*>(aProxy
->GetWrapper());
953 aProxy
->SetWrapper(0);
956 void a11y::PlatformEvent(Accessible
* aTarget
, uint32_t aEventType
) {
957 AtkObject
* wrapper
= GetWrapperFor(aTarget
);
959 switch (aEventType
) {
960 case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE
:
961 if (aTarget
->IsDoc()) {
962 g_signal_emit_by_name(wrapper
, "load_complete");
964 // XXX - Handle native dialog accessibles.
965 if (!aTarget
->IsRoot() && aTarget
->HasARIARole() &&
966 aTarget
->Role() == roles::DIALOG
) {
967 guint id
= g_signal_lookup("activate", MAI_TYPE_ATK_OBJECT
);
968 g_signal_emit(wrapper
, id
, 0);
971 case nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD
:
972 if (aTarget
->IsDoc()) {
973 g_signal_emit_by_name(wrapper
, "reload");
976 case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED
:
977 if (aTarget
->IsDoc()) {
978 g_signal_emit_by_name(wrapper
, "load_stopped");
981 case nsIAccessibleEvent::EVENT_MENUPOPUP_START
:
982 atk_focus_tracker_notify(wrapper
); // fire extra focus event
983 atk_object_notify_state_change(wrapper
, ATK_STATE_VISIBLE
, true);
984 atk_object_notify_state_change(wrapper
, ATK_STATE_SHOWING
, true);
986 case nsIAccessibleEvent::EVENT_MENUPOPUP_END
:
987 atk_object_notify_state_change(wrapper
, ATK_STATE_VISIBLE
, false);
988 atk_object_notify_state_change(wrapper
, ATK_STATE_SHOWING
, false);
990 case nsIAccessibleEvent::EVENT_ALERT
:
991 // A hack using state change showing events as alert events.
992 atk_object_notify_state_change(wrapper
, ATK_STATE_SHOWING
, true);
994 case nsIAccessibleEvent::EVENT_VALUE_CHANGE
:
995 case nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE
:
996 if (aTarget
->HasNumericValue()) {
997 // Make sure this is a numeric value. Don't fire for string value
998 // changes (e.g. text editing) ATK values are always numeric.
999 g_object_notify((GObject
*)wrapper
, "accessible-value");
1002 case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED
:
1003 g_signal_emit_by_name(wrapper
, "text_selection_changed");
1005 case nsIAccessibleEvent::EVENT_SELECTION_WITHIN
:
1006 g_signal_emit_by_name(wrapper
, "selection_changed");
1008 case nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED
:
1009 g_signal_emit_by_name(wrapper
, "text-attributes-changed");
1011 case nsIAccessibleEvent::EVENT_NAME_CHANGE
: {
1012 nsAutoString newName
;
1013 aTarget
->Name(newName
);
1014 MaybeFireNameChange(wrapper
, newName
);
1017 case nsIAccessibleEvent::EVENT_WINDOW_ACTIVATE
: {
1018 guint id
= g_signal_lookup("activate", MAI_TYPE_ATK_OBJECT
);
1019 g_signal_emit(wrapper
, id
, 0);
1020 // Always fire a current focus event after activation.
1021 FocusMgr()->ForceFocusEvent();
1024 case nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE
: {
1025 guint id
= g_signal_lookup("deactivate", MAI_TYPE_ATK_OBJECT
);
1026 g_signal_emit(wrapper
, id
, 0);
1029 case nsIAccessibleEvent::EVENT_WINDOW_MAXIMIZE
: {
1030 guint id
= g_signal_lookup("maximize", MAI_TYPE_ATK_OBJECT
);
1031 g_signal_emit(wrapper
, id
, 0);
1034 case nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE
: {
1035 guint id
= g_signal_lookup("minimize", MAI_TYPE_ATK_OBJECT
);
1036 g_signal_emit(wrapper
, id
, 0);
1039 case nsIAccessibleEvent::EVENT_WINDOW_RESTORE
: {
1040 guint id
= g_signal_lookup("restore", MAI_TYPE_ATK_OBJECT
);
1041 g_signal_emit(wrapper
, id
, 0);
1047 void a11y::PlatformStateChangeEvent(Accessible
* aTarget
, uint64_t aState
,
1049 MaiAtkObject
* atkObj
= MAI_ATK_OBJECT(GetWrapperFor(aTarget
));
1050 atkObj
->FireStateChangeEvent(aState
, aEnabled
);
1053 void a11y::PlatformFocusEvent(Accessible
* aTarget
,
1054 const LayoutDeviceIntRect
& aCaretRect
) {
1055 AtkObject
* wrapper
= GetWrapperFor(aTarget
);
1057 // XXX Do we really need this check? If so, do we need a similar check for
1058 // RemoteAccessible?
1059 if (LocalAccessible
* localTarget
= aTarget
->AsLocal()) {
1060 a11y::RootAccessible
* rootAcc
= localTarget
->RootAccessible();
1061 if (!rootAcc
|| !rootAcc
->IsActivated()) {
1066 atk_focus_tracker_notify(wrapper
);
1067 atk_object_notify_state_change(wrapper
, ATK_STATE_FOCUSED
, true);
1070 void a11y::PlatformCaretMoveEvent(Accessible
* aTarget
, int32_t aOffset
,
1071 bool aIsSelectionCollapsed
,
1072 int32_t aGranularity
,
1073 const LayoutDeviceIntRect
& aCaretRect
,
1075 AtkObject
* wrapper
= GetWrapperFor(aTarget
);
1076 g_signal_emit_by_name(wrapper
, "text_caret_moved", aOffset
);
1079 void MaiAtkObject::FireStateChangeEvent(uint64_t aState
, bool aEnabled
) {
1080 auto state
= aState
;
1081 int32_t stateIndex
= -1;
1088 stateIndex
>= 0 && stateIndex
< static_cast<int32_t>(gAtkStateMapLen
),
1089 "No ATK state for internal state was found");
1090 if (stateIndex
< 0 || stateIndex
>= static_cast<int32_t>(gAtkStateMapLen
)) {
1094 if (gAtkStateMap
[stateIndex
].atkState
!= kNone
) {
1095 MOZ_ASSERT(gAtkStateMap
[stateIndex
].stateMapEntryType
!= kNoStateChange
,
1096 "State changes should not fired for this state");
1098 if (gAtkStateMap
[stateIndex
].stateMapEntryType
== kMapOpposite
) {
1099 aEnabled
= !aEnabled
;
1102 // Fire state change for first state if there is one to map
1103 atk_object_notify_state_change(&parent
, gAtkStateMap
[stateIndex
].atkState
,
1108 void a11y::PlatformTextChangeEvent(Accessible
* aTarget
, const nsAString
& aStr
,
1109 int32_t aStart
, uint32_t aLen
,
1110 bool aIsInsert
, bool aFromUser
) {
1111 MaiAtkObject
* atkObj
= MAI_ATK_OBJECT(GetWrapperFor(aTarget
));
1112 atkObj
->FireTextChangeEvent(aStr
, aStart
, aLen
, aIsInsert
, aFromUser
);
1115 #define OLD_TEXT_INSERTED "text_changed::insert"
1116 #define OLD_TEXT_REMOVED "text_changed::delete"
1117 static const char* oldTextChangeStrings
[2][2] = {
1118 {OLD_TEXT_REMOVED NON_USER_EVENT
, OLD_TEXT_INSERTED NON_USER_EVENT
},
1119 {OLD_TEXT_REMOVED
, OLD_TEXT_INSERTED
}};
1121 #define TEXT_INSERTED "text-insert"
1122 #define TEXT_REMOVED "text-remove"
1123 #define NON_USER_DETAIL "::system"
1124 static const char* textChangedStrings
[2][2] = {
1125 {TEXT_REMOVED NON_USER_DETAIL
, TEXT_INSERTED NON_USER_DETAIL
},
1126 {TEXT_REMOVED
, TEXT_INSERTED
}};
1128 void MaiAtkObject::FireTextChangeEvent(const nsAString
& aStr
, int32_t aStart
,
1129 uint32_t aLen
, bool aIsInsert
,
1131 if (gAvailableAtkSignals
== eUnknown
) {
1132 gAvailableAtkSignals
= g_signal_lookup("text-insert", G_OBJECT_TYPE(this))
1133 ? eHaveNewAtkTextSignals
1137 if (gAvailableAtkSignals
== eNoNewAtkSignals
) {
1138 // XXX remove this code and the gHaveNewTextSignals check when we can
1139 // stop supporting old atk since it doesn't really work anyway
1141 const char* signal_name
= oldTextChangeStrings
[aFromUser
][aIsInsert
];
1142 g_signal_emit_by_name(this, signal_name
, aStart
, aLen
);
1144 const char* signal_name
= textChangedStrings
[aFromUser
][aIsInsert
];
1145 g_signal_emit_by_name(this, signal_name
, aStart
, aLen
,
1146 NS_ConvertUTF16toUTF8(aStr
).get());
1150 void a11y::PlatformShowHideEvent(Accessible
* aTarget
, Accessible
* aParent
,
1151 bool aInsert
, bool aFromUser
) {
1152 AtkObject
* atkObj
= GetWrapperFor(aTarget
);
1154 // XXX - Handle native dialog accessibles.
1155 if (!aTarget
->IsRoot() && aTarget
->HasARIARole() &&
1156 aTarget
->Role() == roles::DIALOG
) {
1157 guint id
= g_signal_lookup("deactivate", MAI_TYPE_ATK_OBJECT
);
1158 g_signal_emit(atkObj
, id
, 0);
1162 MaiAtkObject
* obj
= MAI_ATK_OBJECT(atkObj
);
1163 obj
->FireAtkShowHideEvent(GetWrapperFor(aParent
), aInsert
, aFromUser
);
1166 #define ADD_EVENT "children_changed::add"
1167 #define HIDE_EVENT "children_changed::remove"
1169 static const char* kMutationStrings
[2][2] = {
1170 {HIDE_EVENT NON_USER_EVENT
, ADD_EVENT NON_USER_EVENT
},
1171 {HIDE_EVENT
, ADD_EVENT
},
1174 void MaiAtkObject::FireAtkShowHideEvent(AtkObject
* aParent
, bool aIsAdded
,
1177 // XXX ATK needs a parent for these events. However, we might have already
1178 // unbound from the parent by the time we fire a hide event. Ideally, we
1179 // need to find a way to keep the parent around, but ATK clients don't seem
1180 // to care about these missing events.
1181 MOZ_ASSERT(!aIsAdded
);
1184 int32_t indexInParent
= getIndexInParentCB(&this->parent
);
1185 const char* signal_name
= kMutationStrings
[aFromUser
][aIsAdded
];
1186 g_signal_emit_by_name(aParent
, signal_name
, indexInParent
, this, nullptr);
1189 void a11y::PlatformSelectionEvent(Accessible
*, Accessible
* aWidget
, uint32_t) {
1190 MaiAtkObject
* obj
= MAI_ATK_OBJECT(GetWrapperFor(aWidget
));
1191 g_signal_emit_by_name(obj
, "selection_changed");
1195 void AccessibleWrap::GetKeyBinding(Accessible
* aAccessible
,
1196 nsAString
& aResult
) {
1197 // Return all key bindings including access key and keyboard shortcut.
1200 nsAutoString keyBindingsStr
;
1201 KeyBinding keyBinding
= aAccessible
->AccessKey();
1202 if (!keyBinding
.IsEmpty()) {
1203 keyBinding
.AppendToString(keyBindingsStr
, KeyBinding::eAtkFormat
);
1205 Accessible
* parent
= aAccessible
->Parent();
1206 roles::Role role
= parent
? parent
->Role() : roles::NOTHING
;
1207 if (role
== roles::PARENT_MENUITEM
|| role
== roles::MENUITEM
||
1208 role
== roles::RADIO_MENU_ITEM
|| role
== roles::CHECK_MENU_ITEM
) {
1209 // It is submenu, expose keyboard shortcuts from menu hierarchy like
1211 nsAutoString keysInHierarchyStr
= keyBindingsStr
;
1213 KeyBinding parentKeyBinding
= parent
->AccessKey();
1214 if (!parentKeyBinding
.IsEmpty()) {
1216 parentKeyBinding
.ToString(str
, KeyBinding::eAtkFormat
);
1219 keysInHierarchyStr
.Insert(str
, 0);
1221 } while ((parent
= parent
->Parent()) && parent
->Role() != roles::MENUBAR
);
1223 keyBindingsStr
.Append(';');
1224 keyBindingsStr
.Append(keysInHierarchyStr
);
1227 // No access key, add ';' to point this.
1228 keyBindingsStr
.Append(';');
1231 // Get keyboard shortcut.
1232 keyBindingsStr
.Append(';');
1233 if (LocalAccessible
* localAcc
= aAccessible
->AsLocal()) {
1234 keyBinding
= localAcc
->KeyboardShortcut();
1235 if (!keyBinding
.IsEmpty()) {
1236 keyBinding
.AppendToString(keyBindingsStr
, KeyBinding::eAtkFormat
);
1239 aResult
= keyBindingsStr
;
1243 Accessible
* AccessibleWrap::GetColumnHeader(TableAccessible
* aAccessible
,
1249 Accessible
* cell
= aAccessible
->CellAt(0, aColIdx
);
1254 // If the cell at the first row is column header then assume it is column
1255 // header for all rows,
1256 if (cell
->Role() == roles::COLUMNHEADER
) {
1260 // otherwise get column header for the data cell at the first row.
1261 TableCellAccessible
* tableCell
= cell
->AsTableCell();
1266 AutoTArray
<Accessible
*, 10> headerCells
;
1267 tableCell
->ColHeaderCells(&headerCells
);
1268 if (headerCells
.IsEmpty()) {
1272 return headerCells
[0];
1276 Accessible
* AccessibleWrap::GetRowHeader(TableAccessible
* aAccessible
,
1282 Accessible
* cell
= aAccessible
->CellAt(aRowIdx
, 0);
1287 // If the cell at the first column is row header then assume it is row
1288 // header for all columns,
1289 if (cell
->Role() == roles::ROWHEADER
) {
1293 // otherwise get row header for the data cell at the first column.
1294 TableCellAccessible
* tableCell
= cell
->AsTableCell();
1299 AutoTArray
<Accessible
*, 10> headerCells
;
1300 tableCell
->RowHeaderCells(&headerCells
);
1301 if (headerCells
.IsEmpty()) {
1305 return headerCells
[0];