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 "ApplicationAccessibleWrap.h"
11 #include "InterfaceInitFuncs.h"
12 #include "nsAccUtils.h"
13 #include "mozilla/a11y/PDocAccessible.h"
14 #include "OuterDocAccessible.h"
15 #include "RemoteAccessible.h"
16 #include "DocAccessibleParent.h"
17 #include "RootAccessible.h"
18 #include "TableAccessible.h"
19 #include "TableCellAccessible.h"
21 #include "nsMaiHyperlink.h"
23 #include "nsStateMap.h"
24 #include "mozilla/a11y/Platform.h"
26 #include "RootAccessible.h"
28 #include "nsISimpleEnumerator.h"
30 #include "mozilla/ArrayUtils.h"
31 #include "mozilla/Sprintf.h"
32 #include "nsComponentManagerUtils.h"
33 #include "nsIPersistentProperties2.h"
35 using namespace mozilla
;
36 using namespace mozilla::a11y
;
38 MaiAtkObject::EAvailableAtkSignals
MaiAtkObject::gAvailableAtkSignals
=
41 // defined in ApplicationAccessibleWrap.cpp
42 extern "C" GType g_atk_hyperlink_impl_type
;
58 enum MaiInterfaceType
{
59 MAI_INTERFACE_COMPONENT
, /* 0 */
62 MAI_INTERFACE_EDITABLE_TEXT
,
63 MAI_INTERFACE_HYPERTEXT
,
64 MAI_INTERFACE_HYPERLINK_IMPL
,
65 MAI_INTERFACE_SELECTION
,
68 MAI_INTERFACE_DOCUMENT
,
69 MAI_INTERFACE_IMAGE
, /* 10 */
70 MAI_INTERFACE_TABLE_CELL
73 static GType
GetAtkTypeForMai(MaiInterfaceType type
) {
75 case MAI_INTERFACE_COMPONENT
:
76 return ATK_TYPE_COMPONENT
;
77 case MAI_INTERFACE_ACTION
:
78 return ATK_TYPE_ACTION
;
79 case MAI_INTERFACE_VALUE
:
80 return ATK_TYPE_VALUE
;
81 case MAI_INTERFACE_EDITABLE_TEXT
:
82 return ATK_TYPE_EDITABLE_TEXT
;
83 case MAI_INTERFACE_HYPERTEXT
:
84 return ATK_TYPE_HYPERTEXT
;
85 case MAI_INTERFACE_HYPERLINK_IMPL
:
86 return g_atk_hyperlink_impl_type
;
87 case MAI_INTERFACE_SELECTION
:
88 return ATK_TYPE_SELECTION
;
89 case MAI_INTERFACE_TABLE
:
90 return ATK_TYPE_TABLE
;
91 case MAI_INTERFACE_TEXT
:
93 case MAI_INTERFACE_DOCUMENT
:
94 return ATK_TYPE_DOCUMENT
;
95 case MAI_INTERFACE_IMAGE
:
96 return ATK_TYPE_IMAGE
;
97 case MAI_INTERFACE_TABLE_CELL
:
100 return G_TYPE_INVALID
;
103 #define NON_USER_EVENT ":system"
105 // The atk interfaces we can expose without checking what version of ATK we are
106 // dealing with. At the moment AtkTableCell is the only interface we can't
108 static const GInterfaceInfo atk_if_infos
[] = {
109 {(GInterfaceInitFunc
)componentInterfaceInitCB
,
110 (GInterfaceFinalizeFunc
) nullptr, nullptr},
111 {(GInterfaceInitFunc
)actionInterfaceInitCB
,
112 (GInterfaceFinalizeFunc
) nullptr, nullptr},
113 {(GInterfaceInitFunc
)valueInterfaceInitCB
, (GInterfaceFinalizeFunc
) nullptr,
115 {(GInterfaceInitFunc
)editableTextInterfaceInitCB
,
116 (GInterfaceFinalizeFunc
) nullptr, nullptr},
117 {(GInterfaceInitFunc
)hypertextInterfaceInitCB
,
118 (GInterfaceFinalizeFunc
) nullptr, nullptr},
119 {(GInterfaceInitFunc
)hyperlinkImplInterfaceInitCB
,
120 (GInterfaceFinalizeFunc
) nullptr, nullptr},
121 {(GInterfaceInitFunc
)selectionInterfaceInitCB
,
122 (GInterfaceFinalizeFunc
) nullptr, nullptr},
123 {(GInterfaceInitFunc
)tableInterfaceInitCB
, (GInterfaceFinalizeFunc
) nullptr,
125 {(GInterfaceInitFunc
)textInterfaceInitCB
, (GInterfaceFinalizeFunc
) nullptr,
127 {(GInterfaceInitFunc
)documentInterfaceInitCB
,
128 (GInterfaceFinalizeFunc
) nullptr, nullptr},
129 {(GInterfaceInitFunc
)imageInterfaceInitCB
, (GInterfaceFinalizeFunc
) nullptr,
132 static GQuark quark_mai_hyperlink
= 0;
134 AtkHyperlink
* MaiAtkObject::GetAtkHyperlink() {
135 NS_ASSERTION(quark_mai_hyperlink
, "quark_mai_hyperlink not initialized");
136 MaiHyperlink
* maiHyperlink
=
137 (MaiHyperlink
*)g_object_get_qdata(G_OBJECT(this), quark_mai_hyperlink
);
139 maiHyperlink
= new MaiHyperlink(accWrap
);
140 g_object_set_qdata(G_OBJECT(this), quark_mai_hyperlink
, maiHyperlink
);
143 return maiHyperlink
->GetAtkHyperlink();
146 void MaiAtkObject::Shutdown() {
148 MaiHyperlink
* maiHyperlink
=
149 (MaiHyperlink
*)g_object_get_qdata(G_OBJECT(this), quark_mai_hyperlink
);
152 g_object_set_qdata(G_OBJECT(this), quark_mai_hyperlink
, nullptr);
156 struct MaiAtkObjectClass
{
157 AtkObjectClass parent_class
;
160 static guint mai_atk_object_signals
[LAST_SIGNAL
] = {
164 static void MaybeFireNameChange(AtkObject
* aAtkObj
, const nsString
& aNewName
);
167 /* callbacks for MaiAtkObject */
168 static void classInitCB(AtkObjectClass
* aClass
);
169 static void initializeCB(AtkObject
* aAtkObj
, gpointer aData
);
170 static void finalizeCB(GObject
* aObj
);
172 /* callbacks for AtkObject virtual functions */
173 static const gchar
* getNameCB(AtkObject
* aAtkObj
);
174 /* getDescriptionCB is also used by image interface */
175 const gchar
* getDescriptionCB(AtkObject
* aAtkObj
);
176 static AtkRole
getRoleCB(AtkObject
* aAtkObj
);
177 static AtkAttributeSet
* getAttributesCB(AtkObject
* aAtkObj
);
178 static const gchar
* GetLocaleCB(AtkObject
*);
179 static AtkObject
* getParentCB(AtkObject
* aAtkObj
);
180 static gint
getChildCountCB(AtkObject
* aAtkObj
);
181 static AtkObject
* refChildCB(AtkObject
* aAtkObj
, gint aChildIndex
);
182 static gint
getIndexInParentCB(AtkObject
* aAtkObj
);
183 static AtkStateSet
* refStateSetCB(AtkObject
* aAtkObj
);
184 static AtkRelationSet
* refRelationSetCB(AtkObject
* aAtkObj
);
186 /* the missing atkobject virtual functions */
188 static AtkLayer getLayerCB(AtkObject *aAtkObj);
189 static gint getMdiZorderCB(AtkObject *aAtkObj);
190 static void SetNameCB(AtkObject *aAtkObj,
192 static void SetDescriptionCB(AtkObject *aAtkObj,
193 const gchar *description);
194 static void SetParentCB(AtkObject *aAtkObj,
196 static void SetRoleCB(AtkObject *aAtkObj,
198 static guint ConnectPropertyChangeHandlerCB(
200 AtkPropertyChangeHandler *handler);
201 static void RemovePropertyChangeHandlerCB(
204 static void InitializeCB(AtkObject *aAtkObj,
206 static void ChildrenChangedCB(AtkObject *aAtkObj,
208 gpointer changed_child);
209 static void FocusEventCB(AtkObject *aAtkObj,
211 static void PropertyChangeCB(AtkObject *aAtkObj,
212 AtkPropertyValues *values);
213 static void StateChangeCB(AtkObject *aAtkObj,
216 static void VisibleDataChangedCB(AtkObject *aAtkObj);
220 static GType
GetMaiAtkType(uint16_t interfacesBits
);
221 static const char* GetUniqueMaiAtkTypeName(uint16_t interfacesBits
);
223 static gpointer parent_class
= nullptr;
225 GType
mai_atk_object_get_type(void) {
226 static GType type
= 0;
229 static const GTypeInfo tinfo
= {
230 sizeof(MaiAtkObjectClass
),
231 (GBaseInitFunc
) nullptr,
232 (GBaseFinalizeFunc
) nullptr,
233 (GClassInitFunc
)classInitCB
,
234 (GClassFinalizeFunc
) nullptr,
235 nullptr, /* class data */
236 sizeof(MaiAtkObject
), /* instance size */
237 0, /* nb preallocs */
238 (GInstanceInitFunc
) nullptr,
239 nullptr /* value table */
242 type
= g_type_register_static(ATK_TYPE_OBJECT
, "MaiAtkObject", &tinfo
,
244 quark_mai_hyperlink
= g_quark_from_static_string("MaiHyperlink");
249 AccessibleWrap::AccessibleWrap(nsIContent
* aContent
, DocAccessible
* aDoc
)
250 : LocalAccessible(aContent
, aDoc
), mAtkObject(nullptr) {}
252 AccessibleWrap::~AccessibleWrap() {
253 NS_ASSERTION(!mAtkObject
, "ShutdownAtkObject() is not called");
256 void AccessibleWrap::ShutdownAtkObject() {
257 if (!mAtkObject
) return;
259 NS_ASSERTION(IS_MAI_OBJECT(mAtkObject
), "wrong type of atk object");
260 if (IS_MAI_OBJECT(mAtkObject
)) MAI_ATK_OBJECT(mAtkObject
)->Shutdown();
262 g_object_unref(mAtkObject
);
263 mAtkObject
= nullptr;
266 void AccessibleWrap::Shutdown() {
268 LocalAccessible::Shutdown();
271 void AccessibleWrap::GetNativeInterface(void** aOutAccessible
) {
272 *aOutAccessible
= nullptr;
275 if (IsDefunct() || IsText()) {
276 // We don't create ATK objects for node which has been shutdown or
281 GType type
= GetMaiAtkType(CreateMaiInterfaces());
284 mAtkObject
= reinterpret_cast<AtkObject
*>(g_object_new(type
, nullptr));
285 if (!mAtkObject
) return;
287 atk_object_initialize(mAtkObject
, this);
288 mAtkObject
->role
= ATK_ROLE_INVALID
;
289 mAtkObject
->layer
= ATK_LAYER_INVALID
;
292 *aOutAccessible
= mAtkObject
;
295 AtkObject
* AccessibleWrap::GetAtkObject(void) {
296 void* atkObj
= nullptr;
297 GetNativeInterface(&atkObj
);
298 return static_cast<AtkObject
*>(atkObj
);
301 // Get AtkObject from LocalAccessible interface
303 AtkObject
* AccessibleWrap::GetAtkObject(LocalAccessible
* acc
) {
304 void* atkObjPtr
= nullptr;
305 acc
->GetNativeInterface(&atkObjPtr
);
306 return atkObjPtr
? ATK_OBJECT(atkObjPtr
) : nullptr;
310 uint16_t AccessibleWrap::CreateMaiInterfaces(void) {
311 uint16_t interfacesBits
= 0;
313 // The Component interface is supported by all accessibles.
314 interfacesBits
|= 1 << MAI_INTERFACE_COMPONENT
;
316 // Add Action interface if the action count is more than zero.
317 if (ActionCount() > 0) interfacesBits
|= 1 << MAI_INTERFACE_ACTION
;
319 // Text, Editabletext, and Hypertext interface.
320 HyperTextAccessible
* hyperText
= AsHyperText();
321 if (hyperText
&& hyperText
->IsTextRole()) {
322 interfacesBits
|= 1 << MAI_INTERFACE_TEXT
;
323 interfacesBits
|= 1 << MAI_INTERFACE_EDITABLE_TEXT
;
324 if (!nsAccUtils::MustPrune(this)) {
325 interfacesBits
|= 1 << MAI_INTERFACE_HYPERTEXT
;
330 if (HasNumericValue()) interfacesBits
|= 1 << MAI_INTERFACE_VALUE
;
332 // Document interface.
333 if (IsDoc()) interfacesBits
|= 1 << MAI_INTERFACE_DOCUMENT
;
335 if (IsImage()) interfacesBits
|= 1 << MAI_INTERFACE_IMAGE
;
337 // HyperLink interface.
338 if (IsLink()) interfacesBits
|= 1 << MAI_INTERFACE_HYPERLINK_IMPL
;
340 if (!nsAccUtils::MustPrune(this)) { // These interfaces require children
342 if (AsTable()) interfacesBits
|= 1 << MAI_INTERFACE_TABLE
;
344 if (AsTableCell()) interfacesBits
|= 1 << MAI_INTERFACE_TABLE_CELL
;
346 // Selection interface.
348 interfacesBits
|= 1 << MAI_INTERFACE_SELECTION
;
352 return interfacesBits
;
355 static GType
GetMaiAtkType(uint16_t interfacesBits
) {
357 static const GTypeInfo tinfo
= {
358 sizeof(MaiAtkObjectClass
),
359 (GBaseInitFunc
) nullptr,
360 (GBaseFinalizeFunc
) nullptr,
361 (GClassInitFunc
) nullptr,
362 (GClassFinalizeFunc
) nullptr,
363 nullptr, /* class data */
364 sizeof(MaiAtkObject
), /* instance size */
365 0, /* nb preallocs */
366 (GInstanceInitFunc
) nullptr,
367 nullptr /* value table */
371 * The members we use to register GTypes are GetAtkTypeForMai
372 * and atk_if_infos, which are constant values to each MaiInterface
373 * So we can reuse the registered GType when having
374 * the same MaiInterface types.
376 const char* atkTypeName
= GetUniqueMaiAtkTypeName(interfacesBits
);
377 type
= g_type_from_name(atkTypeName
);
383 * gobject limits the number of types that can directly derive from any
384 * given object type to 4095.
386 static uint16_t typeRegCount
= 0;
387 if (typeRegCount
++ >= 4095) {
388 return G_TYPE_INVALID
;
390 type
= g_type_register_static(MAI_TYPE_ATK_OBJECT
, atkTypeName
, &tinfo
,
393 for (uint32_t index
= 0; index
< ArrayLength(atk_if_infos
); index
++) {
394 if (interfacesBits
& (1 << index
)) {
395 g_type_add_interface_static(type
,
396 GetAtkTypeForMai((MaiInterfaceType
)index
),
397 &atk_if_infos
[index
]);
401 // Special case AtkTableCell so we can check what version of Atk we are
403 if (IsAtkVersionAtLeast(2, 12) &&
404 (interfacesBits
& (1 << MAI_INTERFACE_TABLE_CELL
))) {
405 const GInterfaceInfo cellInfo
= {
406 (GInterfaceInitFunc
)tableCellInterfaceInitCB
,
407 (GInterfaceFinalizeFunc
) nullptr, nullptr};
408 g_type_add_interface_static(type
, gAtkTableCellGetTypeFunc(), &cellInfo
);
414 static const char* GetUniqueMaiAtkTypeName(uint16_t interfacesBits
) {
415 #define MAI_ATK_TYPE_NAME_LEN (30) /* 10+sizeof(uint16_t)*8/4+1 < 30 */
417 static gchar namePrefix
[] = "MaiAtkType"; /* size = 10 */
418 static gchar name
[MAI_ATK_TYPE_NAME_LEN
+ 1];
420 SprintfLiteral(name
, "%s%x", namePrefix
, interfacesBits
);
421 name
[MAI_ATK_TYPE_NAME_LEN
] = '\0';
426 bool AccessibleWrap::IsValidObject() {
427 // to ensure we are not shut down
431 /* static functions for ATK callbacks */
432 void classInitCB(AtkObjectClass
* aClass
) {
433 GObjectClass
* gobject_class
= G_OBJECT_CLASS(aClass
);
435 parent_class
= g_type_class_peek_parent(aClass
);
437 aClass
->get_name
= getNameCB
;
438 aClass
->get_description
= getDescriptionCB
;
439 aClass
->get_parent
= getParentCB
;
440 aClass
->get_n_children
= getChildCountCB
;
441 aClass
->ref_child
= refChildCB
;
442 aClass
->get_index_in_parent
= getIndexInParentCB
;
443 aClass
->get_role
= getRoleCB
;
444 aClass
->get_attributes
= getAttributesCB
;
445 aClass
->get_object_locale
= GetLocaleCB
;
446 aClass
->ref_state_set
= refStateSetCB
;
447 aClass
->ref_relation_set
= refRelationSetCB
;
449 aClass
->initialize
= initializeCB
;
451 gobject_class
->finalize
= finalizeCB
;
453 mai_atk_object_signals
[ACTIVATE
] = g_signal_new(
454 "activate", MAI_TYPE_ATK_OBJECT
, G_SIGNAL_RUN_LAST
,
455 0, /* default signal handler */
456 nullptr, nullptr, g_cclosure_marshal_VOID__VOID
, G_TYPE_NONE
, 0);
457 mai_atk_object_signals
[CREATE
] = g_signal_new(
458 "create", MAI_TYPE_ATK_OBJECT
, G_SIGNAL_RUN_LAST
,
459 0, /* default signal handler */
460 nullptr, nullptr, g_cclosure_marshal_VOID__VOID
, G_TYPE_NONE
, 0);
461 mai_atk_object_signals
[DEACTIVATE
] = g_signal_new(
462 "deactivate", MAI_TYPE_ATK_OBJECT
, G_SIGNAL_RUN_LAST
,
463 0, /* default signal handler */
464 nullptr, nullptr, g_cclosure_marshal_VOID__VOID
, G_TYPE_NONE
, 0);
465 mai_atk_object_signals
[DESTROY
] = g_signal_new(
466 "destroy", MAI_TYPE_ATK_OBJECT
, G_SIGNAL_RUN_LAST
,
467 0, /* default signal handler */
468 nullptr, nullptr, g_cclosure_marshal_VOID__VOID
, G_TYPE_NONE
, 0);
469 mai_atk_object_signals
[MAXIMIZE
] = g_signal_new(
470 "maximize", MAI_TYPE_ATK_OBJECT
, G_SIGNAL_RUN_LAST
,
471 0, /* default signal handler */
472 nullptr, nullptr, g_cclosure_marshal_VOID__VOID
, G_TYPE_NONE
, 0);
473 mai_atk_object_signals
[MINIMIZE
] = g_signal_new(
474 "minimize", MAI_TYPE_ATK_OBJECT
, G_SIGNAL_RUN_LAST
,
475 0, /* default signal handler */
476 nullptr, nullptr, g_cclosure_marshal_VOID__VOID
, G_TYPE_NONE
, 0);
477 mai_atk_object_signals
[RESIZE
] = g_signal_new(
478 "resize", MAI_TYPE_ATK_OBJECT
, G_SIGNAL_RUN_LAST
,
479 0, /* default signal handler */
480 nullptr, nullptr, g_cclosure_marshal_VOID__VOID
, G_TYPE_NONE
, 0);
481 mai_atk_object_signals
[RESTORE
] = g_signal_new(
482 "restore", MAI_TYPE_ATK_OBJECT
, G_SIGNAL_RUN_LAST
,
483 0, /* default signal handler */
484 nullptr, nullptr, g_cclosure_marshal_VOID__VOID
, G_TYPE_NONE
, 0);
487 void initializeCB(AtkObject
* aAtkObj
, gpointer aData
) {
488 NS_ASSERTION((IS_MAI_OBJECT(aAtkObj
)), "Invalid AtkObject");
489 NS_ASSERTION(aData
, "Invalid Data to init AtkObject");
490 if (!aAtkObj
|| !aData
) return;
492 /* call parent init function */
493 /* AtkObjectClass has not a "initialize" function now,
497 if (ATK_OBJECT_CLASS(parent_class
)->initialize
) {
498 ATK_OBJECT_CLASS(parent_class
)->initialize(aAtkObj
, aData
);
501 /* initialize object */
502 MAI_ATK_OBJECT(aAtkObj
)->accWrap
.SetBits(reinterpret_cast<uintptr_t>(aData
));
505 void finalizeCB(GObject
* aObj
) {
506 if (!IS_MAI_OBJECT(aObj
)) return;
507 NS_ASSERTION(MAI_ATK_OBJECT(aObj
)->accWrap
.IsNull(), "AccWrap NOT null");
509 // call parent finalize function
510 // finalize of GObjectClass will unref the accessible parent if has
511 if (G_OBJECT_CLASS(parent_class
)->finalize
) {
512 G_OBJECT_CLASS(parent_class
)->finalize(aObj
);
516 const gchar
* getNameCB(AtkObject
* aAtkObj
) {
518 AccessibleWrap
* accWrap
= GetAccessibleWrap(aAtkObj
);
521 } else if (RemoteAccessible
* proxy
= GetProxy(aAtkObj
)) {
527 // XXX Firing an event from here does not seem right
528 MaybeFireNameChange(aAtkObj
, name
);
530 return aAtkObj
->name
;
533 static void MaybeFireNameChange(AtkObject
* aAtkObj
, const nsString
& aNewName
) {
534 NS_ConvertUTF16toUTF8
newNameUTF8(aNewName
);
535 if (aAtkObj
->name
&& !strcmp(aAtkObj
->name
, newNameUTF8
.get())) return;
537 // Below we duplicate the functionality of atk_object_set_name(),
538 // but without calling atk_object_get_name(). Instead of
539 // atk_object_get_name() we directly access aAtkObj->name. This is because
540 // atk_object_get_name() would call getNameCB() which would call
541 // MaybeFireNameChange() (or atk_object_set_name() before this problem was
542 // fixed) and we would get an infinite recursion.
543 // See http://bugzilla.mozilla.org/733712
545 // Do not notify for initial name setting.
546 // See bug http://bugzilla.gnome.org/665870
547 bool notify
= !!aAtkObj
->name
;
550 aAtkObj
->name
= strdup(newNameUTF8
.get());
552 if (notify
) g_object_notify(G_OBJECT(aAtkObj
), "accessible-name");
555 const gchar
* getDescriptionCB(AtkObject
* aAtkObj
) {
556 nsAutoString uniDesc
;
557 AccessibleWrap
* accWrap
= GetAccessibleWrap(aAtkObj
);
559 if (accWrap
->IsDefunct()) return nullptr;
561 accWrap
->Description(uniDesc
);
562 } else if (RemoteAccessible
* proxy
= GetProxy(aAtkObj
)) {
563 proxy
->Description(uniDesc
);
568 NS_ConvertUTF8toUTF16
objDesc(aAtkObj
->description
);
569 if (!uniDesc
.Equals(objDesc
)) {
570 atk_object_set_description(aAtkObj
, NS_ConvertUTF16toUTF8(uniDesc
).get());
573 return aAtkObj
->description
;
576 AtkRole
getRoleCB(AtkObject
* aAtkObj
) {
577 if (aAtkObj
->role
!= ATK_ROLE_INVALID
) return aAtkObj
->role
;
579 AccessibleOrProxy acc
= GetInternalObj(aAtkObj
);
581 return ATK_ROLE_INVALID
;
585 if (AccessibleWrap
* accWrap
= GetAccessibleWrap(aAtkObj
)) {
586 NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(accWrap
),
587 "Does not support Text interface when it should");
591 #define ROLE(geckoRole, stringRole, atkRole, macRole, macSubrole, msaaRole, \
592 ia2Role, androidClass, nameRule) \
593 case roles::geckoRole: \
594 aAtkObj->role = atkRole; \
597 switch (acc
.Role()) {
600 MOZ_CRASH("Unknown role.");
605 if (aAtkObj
->role
== ATK_ROLE_LIST_BOX
&& !IsAtkVersionAtLeast(2, 1)) {
606 aAtkObj
->role
= ATK_ROLE_LIST
;
607 } else if (aAtkObj
->role
== ATK_ROLE_TABLE_ROW
&&
608 !IsAtkVersionAtLeast(2, 1)) {
609 aAtkObj
->role
= ATK_ROLE_LIST_ITEM
;
610 } else if (aAtkObj
->role
== ATK_ROLE_MATH
&& !IsAtkVersionAtLeast(2, 12)) {
611 aAtkObj
->role
= ATK_ROLE_SECTION
;
612 } else if (aAtkObj
->role
== ATK_ROLE_COMMENT
&& !IsAtkVersionAtLeast(2, 12)) {
613 aAtkObj
->role
= ATK_ROLE_SECTION
;
614 } else if (aAtkObj
->role
== ATK_ROLE_LANDMARK
&&
615 !IsAtkVersionAtLeast(2, 12)) {
616 aAtkObj
->role
= ATK_ROLE_SECTION
;
617 } else if (aAtkObj
->role
== ATK_ROLE_FOOTNOTE
&&
618 !IsAtkVersionAtLeast(2, 25, 2)) {
619 aAtkObj
->role
= ATK_ROLE_SECTION
;
620 } else if (aAtkObj
->role
== ATK_ROLE_STATIC
&& !IsAtkVersionAtLeast(2, 16)) {
621 aAtkObj
->role
= ATK_ROLE_TEXT
;
622 } else if ((aAtkObj
->role
== ATK_ROLE_MATH_FRACTION
||
623 aAtkObj
->role
== ATK_ROLE_MATH_ROOT
) &&
624 !IsAtkVersionAtLeast(2, 16)) {
625 aAtkObj
->role
= ATK_ROLE_SECTION
;
626 } else if (aAtkObj
->role
== ATK_ROLE_MARK
&& !IsAtkVersionAtLeast(2, 36)) {
627 aAtkObj
->role
= ATK_ROLE_TEXT
;
628 } else if (aAtkObj
->role
== ATK_ROLE_SUGGESTION
&&
629 !IsAtkVersionAtLeast(2, 36)) {
630 aAtkObj
->role
= ATK_ROLE_SECTION
;
631 } else if (aAtkObj
->role
== ATK_ROLE_COMMENT
&& !IsAtkVersionAtLeast(2, 36)) {
632 aAtkObj
->role
= ATK_ROLE_SECTION
;
635 return aAtkObj
->role
;
638 static AtkAttributeSet
* ConvertToAtkAttributeSet(
639 nsIPersistentProperties
* aAttributes
) {
640 if (!aAttributes
) return nullptr;
642 AtkAttributeSet
* objAttributeSet
= nullptr;
643 nsCOMPtr
<nsISimpleEnumerator
> propEnum
;
644 nsresult rv
= aAttributes
->Enumerate(getter_AddRefs(propEnum
));
645 NS_ENSURE_SUCCESS(rv
, nullptr);
648 while (NS_SUCCEEDED(propEnum
->HasMoreElements(&hasMore
)) && hasMore
) {
649 nsCOMPtr
<nsISupports
> sup
;
650 rv
= propEnum
->GetNext(getter_AddRefs(sup
));
651 NS_ENSURE_SUCCESS(rv
, objAttributeSet
);
653 nsCOMPtr
<nsIPropertyElement
> propElem(do_QueryInterface(sup
));
654 NS_ENSURE_TRUE(propElem
, objAttributeSet
);
657 rv
= propElem
->GetKey(name
);
658 NS_ENSURE_SUCCESS(rv
, objAttributeSet
);
661 rv
= propElem
->GetValue(value
);
662 NS_ENSURE_SUCCESS(rv
, objAttributeSet
);
664 // On ATK, the placeholder attribute is called placeholder-text.
665 if (name
.Equals("placeholder")) {
666 name
.AssignLiteral("placeholder-text");
669 AtkAttribute
* objAttr
= (AtkAttribute
*)g_malloc(sizeof(AtkAttribute
));
670 objAttr
->name
= g_strdup(name
.get());
671 objAttr
->value
= g_strdup(NS_ConvertUTF16toUTF8(value
).get());
672 objAttributeSet
= g_slist_prepend(objAttributeSet
, objAttr
);
675 // libspi will free it
676 return objAttributeSet
;
679 AtkAttributeSet
* GetAttributeSet(LocalAccessible
* aAccessible
) {
680 nsCOMPtr
<nsIPersistentProperties
> attributes
= aAccessible
->Attributes();
681 if (attributes
) return ConvertToAtkAttributeSet(attributes
);
686 AtkAttributeSet
* getAttributesCB(AtkObject
* aAtkObj
) {
687 AccessibleWrap
* accWrap
= GetAccessibleWrap(aAtkObj
);
688 if (accWrap
) return GetAttributeSet(accWrap
);
690 RemoteAccessible
* proxy
= GetProxy(aAtkObj
);
691 if (!proxy
) return nullptr;
693 AutoTArray
<Attribute
, 10> attrs
;
694 proxy
->Attributes(&attrs
);
695 if (attrs
.IsEmpty()) return nullptr;
697 AtkAttributeSet
* objAttributeSet
= nullptr;
698 for (uint32_t i
= 0; i
< attrs
.Length(); i
++) {
699 AtkAttribute
* objAttr
= (AtkAttribute
*)g_malloc(sizeof(AtkAttribute
));
700 // On ATK, the placeholder attribute is called placeholder-text.
701 if (attrs
[i
].Name().Equals("placeholder")) {
702 attrs
[i
].Name().AssignLiteral("placeholder-text");
704 objAttr
->name
= g_strdup(attrs
[i
].Name().get());
705 objAttr
->value
= g_strdup(NS_ConvertUTF16toUTF8(attrs
[i
].Value()).get());
706 objAttributeSet
= g_slist_prepend(objAttributeSet
, objAttr
);
709 return objAttributeSet
;
712 const gchar
* GetLocaleCB(AtkObject
* aAtkObj
) {
713 AccessibleWrap
* accWrap
= GetAccessibleWrap(aAtkObj
);
714 if (!accWrap
) return nullptr;
717 accWrap
->Language(locale
);
718 return AccessibleWrap::ReturnString(locale
);
721 AtkObject
* getParentCB(AtkObject
* aAtkObj
) {
722 if (aAtkObj
->accessible_parent
) return aAtkObj
->accessible_parent
;
724 AccessibleOrProxy acc
= GetInternalObj(aAtkObj
);
729 AccessibleOrProxy parent
= acc
.Parent();
730 AtkObject
* atkParent
= !parent
.IsNull() ? GetWrapperFor(parent
) : nullptr;
731 if (atkParent
) atk_object_set_parent(aAtkObj
, atkParent
);
733 return aAtkObj
->accessible_parent
;
736 gint
getChildCountCB(AtkObject
* aAtkObj
) {
737 if (AccessibleWrap
* accWrap
= GetAccessibleWrap(aAtkObj
)) {
738 if (nsAccUtils::MustPrune(accWrap
)) {
742 uint32_t count
= accWrap
->EmbeddedChildCount();
744 return static_cast<gint
>(count
);
747 OuterDocAccessible
* outerDoc
= accWrap
->AsOuterDoc();
748 if (outerDoc
&& outerDoc
->RemoteChildDoc()) {
753 RemoteAccessible
* proxy
= GetProxy(aAtkObj
);
754 if (proxy
&& !nsAccUtils::MustPrune(proxy
)) {
755 return proxy
->EmbeddedChildCount();
761 AtkObject
* refChildCB(AtkObject
* aAtkObj
, gint aChildIndex
) {
762 // aChildIndex should not be less than zero
763 if (aChildIndex
< 0) {
767 AtkObject
* childAtkObj
= nullptr;
768 AccessibleWrap
* accWrap
= GetAccessibleWrap(aAtkObj
);
770 if (nsAccUtils::MustPrune(accWrap
)) {
774 LocalAccessible
* accChild
= accWrap
->GetEmbeddedChildAt(aChildIndex
);
776 childAtkObj
= AccessibleWrap::GetAtkObject(accChild
);
778 OuterDocAccessible
* docOwner
= accWrap
->AsOuterDoc();
780 RemoteAccessible
* proxyDoc
= docOwner
->RemoteChildDoc();
781 if (proxyDoc
) childAtkObj
= GetWrapperFor(proxyDoc
);
784 } else if (RemoteAccessible
* proxy
= GetProxy(aAtkObj
)) {
785 if (nsAccUtils::MustPrune(proxy
)) {
789 RemoteAccessible
* child
= proxy
->EmbeddedChildAt(aChildIndex
);
790 if (child
) childAtkObj
= GetWrapperFor(child
);
795 NS_ASSERTION(childAtkObj
, "Fail to get AtkObj");
796 if (!childAtkObj
) return nullptr;
798 g_object_ref(childAtkObj
);
800 if (aAtkObj
!= childAtkObj
->accessible_parent
) {
801 atk_object_set_parent(childAtkObj
, aAtkObj
);
807 gint
getIndexInParentCB(AtkObject
* aAtkObj
) {
808 // We don't use LocalAccessible::IndexInParent() because we don't include text
809 // leaf nodes as children in ATK.
810 if (RemoteAccessible
* proxy
= GetProxy(aAtkObj
)) {
811 if (RemoteAccessible
* parent
= proxy
->RemoteParent()) {
812 return parent
->IndexOfEmbeddedChild(proxy
);
815 if (proxy
->OuterDocOfRemoteBrowser()) {
822 AccessibleWrap
* accWrap
= GetAccessibleWrap(aAtkObj
);
827 LocalAccessible
* parent
= accWrap
->LocalParent();
828 if (!parent
) return -1; // No parent
830 return parent
->GetIndexOfEmbeddedChild(accWrap
);
833 static void TranslateStates(uint64_t aState
, roles::Role aRole
,
834 AtkStateSet
* aStateSet
) {
835 // atk doesn't have a read only state so read only things shouldn't be
836 // editable. However, we don't do this for list items because Gecko always
837 // exposes those as read only.
838 if ((aState
& states::READONLY
) && aRole
!= roles::LISTITEM
) {
839 aState
&= ~states::EDITABLE
;
842 // Convert every state to an entry in AtkStateMap
843 uint64_t bitMask
= 1;
844 for (auto stateIndex
= 0U; stateIndex
< gAtkStateMapLen
; stateIndex
++) {
845 if (gAtkStateMap
[stateIndex
]
846 .atkState
) { // There's potentially an ATK state for this
847 bool isStateOn
= (aState
& bitMask
) != 0;
848 if (gAtkStateMap
[stateIndex
].stateMapEntryType
== kMapOpposite
) {
849 isStateOn
= !isStateOn
;
852 atk_state_set_add_state(aStateSet
, gAtkStateMap
[stateIndex
].atkState
);
859 AtkStateSet
* refStateSetCB(AtkObject
* aAtkObj
) {
860 AtkStateSet
* state_set
= nullptr;
861 state_set
= ATK_OBJECT_CLASS(parent_class
)->ref_state_set(aAtkObj
);
863 AccessibleWrap
* accWrap
= GetAccessibleWrap(aAtkObj
);
865 TranslateStates(accWrap
->State(), accWrap
->Role(), state_set
);
866 } else if (RemoteAccessible
* proxy
= GetProxy(aAtkObj
)) {
867 TranslateStates(proxy
->State(), proxy
->Role(), state_set
);
869 TranslateStates(states::DEFUNCT
, roles::NOTHING
, state_set
);
875 static void UpdateAtkRelation(RelationType aType
, LocalAccessible
* aAcc
,
876 AtkRelationType aAtkType
,
877 AtkRelationSet
* aAtkSet
) {
878 if (aAtkType
== ATK_RELATION_NULL
) return;
880 AtkRelation
* atkRelation
=
881 atk_relation_set_get_relation_by_type(aAtkSet
, aAtkType
);
882 if (atkRelation
) atk_relation_set_remove(aAtkSet
, atkRelation
);
884 Relation
rel(aAcc
->RelationByType(aType
));
885 nsTArray
<AtkObject
*> targets
;
886 LocalAccessible
* tempAcc
= nullptr;
887 while ((tempAcc
= rel
.Next())) {
888 targets
.AppendElement(AccessibleWrap::GetAtkObject(tempAcc
));
891 if (aType
== RelationType::EMBEDS
&& aAcc
->IsRoot()) {
892 if (RemoteAccessible
* proxyDoc
=
893 aAcc
->AsRoot()->GetPrimaryRemoteTopLevelContentDoc()) {
894 targets
.AppendElement(GetWrapperFor(proxyDoc
));
898 if (targets
.Length()) {
900 atk_relation_new(targets
.Elements(), targets
.Length(), aAtkType
);
901 atk_relation_set_add(aAtkSet
, atkRelation
);
902 g_object_unref(atkRelation
);
906 AtkRelationSet
* refRelationSetCB(AtkObject
* aAtkObj
) {
907 AtkRelationSet
* relation_set
=
908 ATK_OBJECT_CLASS(parent_class
)->ref_relation_set(aAtkObj
);
910 const AtkRelationType typeMap
[] = {
911 #define RELATIONTYPE(gecko, s, atk, m, i) atk,
912 #include "RelationTypeMap.h"
916 if (RemoteAccessible
* proxy
= GetProxy(aAtkObj
)) {
917 nsTArray
<RelationType
> types
;
918 nsTArray
<nsTArray
<RemoteAccessible
*>> targetSets
;
919 proxy
->Relations(&types
, &targetSets
);
921 size_t relationCount
= types
.Length();
922 for (size_t i
= 0; i
< relationCount
; i
++) {
923 if (typeMap
[static_cast<uint32_t>(types
[i
])] == ATK_RELATION_NULL
) {
927 size_t targetCount
= targetSets
[i
].Length();
928 AutoTArray
<AtkObject
*, 5> wrappers
;
929 for (size_t j
= 0; j
< targetCount
; j
++) {
930 wrappers
.AppendElement(GetWrapperFor(targetSets
[i
][j
]));
933 AtkRelationType atkType
= typeMap
[static_cast<uint32_t>(types
[i
])];
934 AtkRelation
* atkRelation
=
935 atk_relation_set_get_relation_by_type(relation_set
, atkType
);
936 if (atkRelation
) atk_relation_set_remove(relation_set
, atkRelation
);
939 atk_relation_new(wrappers
.Elements(), wrappers
.Length(), atkType
);
940 atk_relation_set_add(relation_set
, atkRelation
);
941 g_object_unref(atkRelation
);
945 AccessibleWrap
* accWrap
= GetAccessibleWrap(aAtkObj
);
946 if (!accWrap
) return relation_set
;
948 #define RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) \
949 UpdateAtkRelation(RelationType::geckoType, accWrap, atkType, relation_set);
951 #include "RelationTypeMap.h"
958 // Check if aAtkObj is a valid MaiAtkObject, and return the AccessibleWrap
960 AccessibleWrap
* GetAccessibleWrap(AtkObject
* aAtkObj
) {
961 bool isMAIObject
= IS_MAI_OBJECT(aAtkObj
);
962 NS_ENSURE_TRUE(isMAIObject
|| MAI_IS_ATK_SOCKET(aAtkObj
), nullptr);
964 AccessibleWrap
* accWrap
= nullptr;
966 LocalAccessible
* acc
= MAI_ATK_OBJECT(aAtkObj
)->accWrap
.AsAccessible();
967 accWrap
= static_cast<AccessibleWrap
*>(acc
);
969 accWrap
= MAI_ATK_SOCKET(aAtkObj
)->accWrap
;
972 // Check if the accessible was deconstructed.
973 if (!accWrap
) return nullptr;
975 NS_ENSURE_TRUE(accWrap
->GetAtkObject() == aAtkObj
, nullptr);
977 AccessibleWrap
* appAccWrap
= ApplicationAcc();
978 if (appAccWrap
!= accWrap
&& !accWrap
->IsValidObject()) return nullptr;
983 RemoteAccessible
* GetProxy(AtkObject
* aObj
) {
984 return GetInternalObj(aObj
).AsProxy();
987 AccessibleOrProxy
GetInternalObj(AtkObject
* aObj
) {
988 if (!aObj
|| !IS_MAI_OBJECT(aObj
)) return nullptr;
990 return MAI_ATK_OBJECT(aObj
)->accWrap
;
993 AtkObject
* GetWrapperFor(RemoteAccessible
* aProxy
) {
994 return reinterpret_cast<AtkObject
*>(aProxy
->GetWrapper() & ~IS_PROXY
);
997 AtkObject
* GetWrapperFor(AccessibleOrProxy aObj
) {
998 if (aObj
.IsProxy()) {
999 return GetWrapperFor(aObj
.AsProxy());
1002 return AccessibleWrap::GetAtkObject(aObj
.AsAccessible());
1005 static uint16_t GetInterfacesForProxy(RemoteAccessible
* aProxy
) {
1006 uint16_t interfaces
= 1 << MAI_INTERFACE_COMPONENT
;
1007 if (aProxy
->IsHyperText()) {
1008 interfaces
|= (1 << MAI_INTERFACE_HYPERTEXT
) | (1 << MAI_INTERFACE_TEXT
) |
1009 (1 << MAI_INTERFACE_EDITABLE_TEXT
);
1012 if (aProxy
->IsLink()) {
1013 interfaces
|= 1 << MAI_INTERFACE_HYPERLINK_IMPL
;
1016 if (aProxy
->HasNumericValue()) {
1017 interfaces
|= 1 << MAI_INTERFACE_VALUE
;
1020 if (aProxy
->IsTable()) {
1021 interfaces
|= 1 << MAI_INTERFACE_TABLE
;
1024 if (aProxy
->IsTableCell()) {
1025 interfaces
|= 1 << MAI_INTERFACE_TABLE_CELL
;
1028 if (aProxy
->IsImage()) {
1029 interfaces
|= 1 << MAI_INTERFACE_IMAGE
;
1032 if (aProxy
->IsDoc()) {
1033 interfaces
|= 1 << MAI_INTERFACE_DOCUMENT
;
1036 if (aProxy
->IsSelect()) {
1037 interfaces
|= 1 << MAI_INTERFACE_SELECTION
;
1040 if (aProxy
->IsActionable()) {
1041 interfaces
|= 1 << MAI_INTERFACE_ACTION
;
1047 void a11y::ProxyCreated(RemoteAccessible
* aProxy
) {
1048 GType type
= GetMaiAtkType(GetInterfacesForProxy(aProxy
));
1049 NS_ASSERTION(type
, "why don't we have a type!");
1051 AtkObject
* obj
= reinterpret_cast<AtkObject
*>(g_object_new(type
, nullptr));
1054 uintptr_t inner
= reinterpret_cast<uintptr_t>(aProxy
) | IS_PROXY
;
1055 atk_object_initialize(obj
, reinterpret_cast<gpointer
>(inner
));
1056 obj
->role
= ATK_ROLE_INVALID
;
1057 obj
->layer
= ATK_LAYER_INVALID
;
1058 aProxy
->SetWrapper(reinterpret_cast<uintptr_t>(obj
) | IS_PROXY
);
1061 void a11y::ProxyDestroyed(RemoteAccessible
* aProxy
) {
1062 auto obj
= reinterpret_cast<MaiAtkObject
*>(aProxy
->GetWrapper() & ~IS_PROXY
);
1068 g_object_unref(obj
);
1069 aProxy
->SetWrapper(0);
1072 nsresult
AccessibleWrap::HandleAccEvent(AccEvent
* aEvent
) {
1073 nsresult rv
= LocalAccessible::HandleAccEvent(aEvent
);
1074 NS_ENSURE_SUCCESS(rv
, rv
);
1076 if (IPCAccessibilityActive()) {
1080 LocalAccessible
* accessible
= aEvent
->GetAccessible();
1081 NS_ENSURE_TRUE(accessible
, NS_ERROR_FAILURE
);
1083 // The accessible can become defunct if we have an xpcom event listener
1084 // which decides it would be fun to change the DOM and flush layout.
1085 if (accessible
->IsDefunct()) return NS_OK
;
1087 uint32_t type
= aEvent
->GetEventType();
1089 AtkObject
* atkObj
= AccessibleWrap::GetAtkObject(accessible
);
1091 // We don't create ATK objects for plain text leaves, just return NS_OK in
1094 NS_ASSERTION(type
== nsIAccessibleEvent::EVENT_SHOW
||
1095 type
== nsIAccessibleEvent::EVENT_HIDE
,
1096 "Event other than SHOW and HIDE fired for plain text leaves");
1100 AccessibleWrap
* accWrap
= GetAccessibleWrap(atkObj
);
1102 return NS_OK
; // Node is shut down
1106 case nsIAccessibleEvent::EVENT_STATE_CHANGE
: {
1107 AccStateChangeEvent
* event
= downcast_accEvent(aEvent
);
1108 MAI_ATK_OBJECT(atkObj
)->FireStateChangeEvent(event
->GetState(),
1109 event
->IsStateEnabled());
1113 case nsIAccessibleEvent::EVENT_TEXT_REMOVED
:
1114 case nsIAccessibleEvent::EVENT_TEXT_INSERTED
: {
1115 AccTextChangeEvent
* event
= downcast_accEvent(aEvent
);
1116 NS_ENSURE_TRUE(event
, NS_ERROR_FAILURE
);
1118 MAI_ATK_OBJECT(atkObj
)->FireTextChangeEvent(
1119 event
->ModifiedText(), event
->GetStartOffset(), event
->GetLength(),
1120 event
->IsTextInserted(), event
->IsFromUserInput());
1125 case nsIAccessibleEvent::EVENT_FOCUS
: {
1126 a11y::RootAccessible
* rootAccWrap
= accWrap
->RootAccessible();
1127 if (rootAccWrap
&& rootAccWrap
->mActivated
) {
1128 atk_focus_tracker_notify(atkObj
);
1129 // Fire state change event for focus
1130 atk_object_notify_state_change(atkObj
, ATK_STATE_FOCUSED
, true);
1135 case nsIAccessibleEvent::EVENT_NAME_CHANGE
: {
1136 nsAutoString newName
;
1137 accessible
->Name(newName
);
1139 MaybeFireNameChange(atkObj
, newName
);
1144 case nsIAccessibleEvent::EVENT_VALUE_CHANGE
:
1145 case nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE
:
1146 if (accessible
->HasNumericValue()) {
1147 // Make sure this is a numeric value. Don't fire for string value
1148 // changes (e.g. text editing) ATK values are always numeric.
1149 g_object_notify((GObject
*)atkObj
, "accessible-value");
1153 case nsIAccessibleEvent::EVENT_SELECTION
:
1154 case nsIAccessibleEvent::EVENT_SELECTION_ADD
:
1155 case nsIAccessibleEvent::EVENT_SELECTION_REMOVE
: {
1156 // XXX: dupe events may be fired
1157 AccSelChangeEvent
* selChangeEvent
= downcast_accEvent(aEvent
);
1158 g_signal_emit_by_name(
1159 AccessibleWrap::GetAtkObject(selChangeEvent
->Widget()),
1160 "selection_changed");
1164 case nsIAccessibleEvent::EVENT_SELECTION_WITHIN
: {
1165 g_signal_emit_by_name(atkObj
, "selection_changed");
1169 case nsIAccessibleEvent::EVENT_ALERT
:
1170 // A hack using state change showing events as alert events.
1171 atk_object_notify_state_change(atkObj
, ATK_STATE_SHOWING
, true);
1174 case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED
:
1175 g_signal_emit_by_name(atkObj
, "text_selection_changed");
1178 case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED
: {
1179 AccCaretMoveEvent
* caretMoveEvent
= downcast_accEvent(aEvent
);
1180 NS_ASSERTION(caretMoveEvent
, "Event needs event data");
1181 if (!caretMoveEvent
) break;
1183 int32_t caretOffset
= caretMoveEvent
->GetCaretOffset();
1184 g_signal_emit_by_name(atkObj
, "text_caret_moved", caretOffset
);
1187 case nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED
:
1188 g_signal_emit_by_name(atkObj
, "text-attributes-changed");
1191 case nsIAccessibleEvent::EVENT_TABLE_MODEL_CHANGED
:
1192 g_signal_emit_by_name(atkObj
, "model_changed");
1195 case nsIAccessibleEvent::EVENT_TABLE_ROW_INSERT
: {
1196 AccTableChangeEvent
* tableEvent
= downcast_accEvent(aEvent
);
1197 NS_ENSURE_TRUE(tableEvent
, NS_ERROR_FAILURE
);
1199 int32_t rowIndex
= tableEvent
->GetIndex();
1200 int32_t numRows
= tableEvent
->GetCount();
1202 g_signal_emit_by_name(atkObj
, "row_inserted", rowIndex
, numRows
);
1205 case nsIAccessibleEvent::EVENT_TABLE_ROW_DELETE
: {
1206 AccTableChangeEvent
* tableEvent
= downcast_accEvent(aEvent
);
1207 NS_ENSURE_TRUE(tableEvent
, NS_ERROR_FAILURE
);
1209 int32_t rowIndex
= tableEvent
->GetIndex();
1210 int32_t numRows
= tableEvent
->GetCount();
1212 g_signal_emit_by_name(atkObj
, "row_deleted", rowIndex
, numRows
);
1215 case nsIAccessibleEvent::EVENT_TABLE_ROW_REORDER
: {
1216 g_signal_emit_by_name(atkObj
, "row_reordered");
1220 case nsIAccessibleEvent::EVENT_TABLE_COLUMN_INSERT
: {
1221 AccTableChangeEvent
* tableEvent
= downcast_accEvent(aEvent
);
1222 NS_ENSURE_TRUE(tableEvent
, NS_ERROR_FAILURE
);
1224 int32_t colIndex
= tableEvent
->GetIndex();
1225 int32_t numCols
= tableEvent
->GetCount();
1226 g_signal_emit_by_name(atkObj
, "column_inserted", colIndex
, numCols
);
1229 case nsIAccessibleEvent::EVENT_TABLE_COLUMN_DELETE
: {
1230 AccTableChangeEvent
* tableEvent
= downcast_accEvent(aEvent
);
1231 NS_ENSURE_TRUE(tableEvent
, NS_ERROR_FAILURE
);
1233 int32_t colIndex
= tableEvent
->GetIndex();
1234 int32_t numCols
= tableEvent
->GetCount();
1235 g_signal_emit_by_name(atkObj
, "column_deleted", colIndex
, numCols
);
1238 case nsIAccessibleEvent::EVENT_TABLE_COLUMN_REORDER
:
1239 g_signal_emit_by_name(atkObj
, "column_reordered");
1242 case nsIAccessibleEvent::EVENT_SECTION_CHANGED
:
1243 g_signal_emit_by_name(atkObj
, "visible_data_changed");
1246 case nsIAccessibleEvent::EVENT_SHOW
: {
1247 AccMutationEvent
* event
= downcast_accEvent(aEvent
);
1248 LocalAccessible
* parentAcc
=
1249 event
? event
->LocalParent() : accessible
->LocalParent();
1250 AtkObject
* parent
= AccessibleWrap::GetAtkObject(parentAcc
);
1251 NS_ENSURE_STATE(parent
);
1252 auto obj
= reinterpret_cast<MaiAtkObject
*>(atkObj
);
1253 obj
->FireAtkShowHideEvent(parent
, true, aEvent
->IsFromUserInput());
1257 case nsIAccessibleEvent::EVENT_HIDE
: {
1258 // XXX - Handle native dialog accessibles.
1259 if (!accessible
->IsRoot() && accessible
->HasARIARole() &&
1260 accessible
->ARIARole() == roles::DIALOG
) {
1261 guint id
= g_signal_lookup("deactivate", MAI_TYPE_ATK_OBJECT
);
1262 g_signal_emit(atkObj
, id
, 0);
1265 AccMutationEvent
* event
= downcast_accEvent(aEvent
);
1266 LocalAccessible
* parentAcc
=
1267 event
? event
->LocalParent() : accessible
->LocalParent();
1268 AtkObject
* parent
= AccessibleWrap::GetAtkObject(parentAcc
);
1269 NS_ENSURE_STATE(parent
);
1270 auto obj
= reinterpret_cast<MaiAtkObject
*>(atkObj
);
1271 obj
->FireAtkShowHideEvent(parent
, false, aEvent
->IsFromUserInput());
1276 * Because dealing with menu is very different between nsIAccessible
1277 * and ATK, and the menu activity is important, specially transfer the
1278 * following two event.
1279 * Need more verification by AT test.
1281 case nsIAccessibleEvent::EVENT_MENU_START
:
1282 case nsIAccessibleEvent::EVENT_MENU_END
:
1285 case nsIAccessibleEvent::EVENT_WINDOW_ACTIVATE
: {
1286 accessible
->AsRoot()->mActivated
= true;
1287 guint id
= g_signal_lookup("activate", MAI_TYPE_ATK_OBJECT
);
1288 g_signal_emit(atkObj
, id
, 0);
1290 // Always fire a current focus event after activation.
1291 FocusMgr()->ForceFocusEvent();
1294 case nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE
: {
1295 accessible
->AsRoot()->mActivated
= false;
1296 guint id
= g_signal_lookup("deactivate", MAI_TYPE_ATK_OBJECT
);
1297 g_signal_emit(atkObj
, id
, 0);
1300 case nsIAccessibleEvent::EVENT_WINDOW_MAXIMIZE
: {
1301 guint id
= g_signal_lookup("maximize", MAI_TYPE_ATK_OBJECT
);
1302 g_signal_emit(atkObj
, id
, 0);
1305 case nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE
: {
1306 guint id
= g_signal_lookup("minimize", MAI_TYPE_ATK_OBJECT
);
1307 g_signal_emit(atkObj
, id
, 0);
1310 case nsIAccessibleEvent::EVENT_WINDOW_RESTORE
: {
1311 guint id
= g_signal_lookup("restore", MAI_TYPE_ATK_OBJECT
);
1312 g_signal_emit(atkObj
, id
, 0);
1315 case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE
:
1316 g_signal_emit_by_name(atkObj
, "load_complete");
1317 // XXX - Handle native dialog accessibles.
1318 if (!accessible
->IsRoot() && accessible
->HasARIARole() &&
1319 accessible
->ARIARole() == roles::DIALOG
) {
1320 guint id
= g_signal_lookup("activate", MAI_TYPE_ATK_OBJECT
);
1321 g_signal_emit(atkObj
, id
, 0);
1325 case nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD
:
1326 g_signal_emit_by_name(atkObj
, "reload");
1329 case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED
:
1330 g_signal_emit_by_name(atkObj
, "load_stopped");
1333 case nsIAccessibleEvent::EVENT_MENUPOPUP_START
:
1334 atk_focus_tracker_notify(atkObj
); // fire extra focus event
1335 atk_object_notify_state_change(atkObj
, ATK_STATE_VISIBLE
, true);
1336 atk_object_notify_state_change(atkObj
, ATK_STATE_SHOWING
, true);
1339 case nsIAccessibleEvent::EVENT_MENUPOPUP_END
:
1340 atk_object_notify_state_change(atkObj
, ATK_STATE_VISIBLE
, false);
1341 atk_object_notify_state_change(atkObj
, ATK_STATE_SHOWING
, false);
1348 void a11y::ProxyEvent(RemoteAccessible
* aTarget
, uint32_t aEventType
) {
1349 AtkObject
* wrapper
= GetWrapperFor(aTarget
);
1351 switch (aEventType
) {
1352 case nsIAccessibleEvent::EVENT_FOCUS
:
1353 atk_focus_tracker_notify(wrapper
);
1354 atk_object_notify_state_change(wrapper
, ATK_STATE_FOCUSED
, true);
1356 case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE
:
1357 g_signal_emit_by_name(wrapper
, "load_complete");
1359 case nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD
:
1360 g_signal_emit_by_name(wrapper
, "reload");
1362 case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED
:
1363 g_signal_emit_by_name(wrapper
, "load_stopped");
1365 case nsIAccessibleEvent::EVENT_MENUPOPUP_START
:
1366 atk_focus_tracker_notify(wrapper
); // fire extra focus event
1367 atk_object_notify_state_change(wrapper
, ATK_STATE_VISIBLE
, true);
1368 atk_object_notify_state_change(wrapper
, ATK_STATE_SHOWING
, true);
1370 case nsIAccessibleEvent::EVENT_MENUPOPUP_END
:
1371 atk_object_notify_state_change(wrapper
, ATK_STATE_VISIBLE
, false);
1372 atk_object_notify_state_change(wrapper
, ATK_STATE_SHOWING
, false);
1374 case nsIAccessibleEvent::EVENT_ALERT
:
1375 // A hack using state change showing events as alert events.
1376 atk_object_notify_state_change(wrapper
, ATK_STATE_SHOWING
, true);
1378 case nsIAccessibleEvent::EVENT_VALUE_CHANGE
:
1379 g_object_notify((GObject
*)wrapper
, "accessible-value");
1381 case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED
:
1382 g_signal_emit_by_name(wrapper
, "text_selection_changed");
1384 case nsIAccessibleEvent::EVENT_SELECTION_WITHIN
:
1385 g_signal_emit_by_name(wrapper
, "selection_changed");
1387 case nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED
:
1388 g_signal_emit_by_name(wrapper
, "text-attributes-changed");
1393 void a11y::ProxyStateChangeEvent(RemoteAccessible
* aTarget
, uint64_t aState
,
1395 MaiAtkObject
* atkObj
= MAI_ATK_OBJECT(GetWrapperFor(aTarget
));
1396 atkObj
->FireStateChangeEvent(aState
, aEnabled
);
1399 void a11y::ProxyCaretMoveEvent(RemoteAccessible
* aTarget
, int32_t aOffset
,
1400 bool aIsSelectionCollapsed
) {
1401 AtkObject
* wrapper
= GetWrapperFor(aTarget
);
1402 g_signal_emit_by_name(wrapper
, "text_caret_moved", aOffset
);
1405 void MaiAtkObject::FireStateChangeEvent(uint64_t aState
, bool aEnabled
) {
1406 auto state
= aState
;
1407 int32_t stateIndex
= -1;
1414 stateIndex
>= 0 && stateIndex
< static_cast<int32_t>(gAtkStateMapLen
),
1415 "No ATK state for internal state was found");
1416 if (stateIndex
< 0 || stateIndex
>= static_cast<int32_t>(gAtkStateMapLen
)) {
1420 if (gAtkStateMap
[stateIndex
].atkState
!= kNone
) {
1421 MOZ_ASSERT(gAtkStateMap
[stateIndex
].stateMapEntryType
!= kNoStateChange
,
1422 "State changes should not fired for this state");
1424 if (gAtkStateMap
[stateIndex
].stateMapEntryType
== kMapOpposite
) {
1425 aEnabled
= !aEnabled
;
1428 // Fire state change for first state if there is one to map
1429 atk_object_notify_state_change(&parent
, gAtkStateMap
[stateIndex
].atkState
,
1434 void a11y::ProxyTextChangeEvent(RemoteAccessible
* aTarget
, const nsString
& aStr
,
1435 int32_t aStart
, uint32_t aLen
, bool aIsInsert
,
1437 MaiAtkObject
* atkObj
= MAI_ATK_OBJECT(GetWrapperFor(aTarget
));
1438 atkObj
->FireTextChangeEvent(aStr
, aStart
, aLen
, aIsInsert
, aFromUser
);
1441 #define OLD_TEXT_INSERTED "text_changed::insert"
1442 #define OLD_TEXT_REMOVED "text_changed::delete"
1443 static const char* oldTextChangeStrings
[2][2] = {
1444 {OLD_TEXT_REMOVED NON_USER_EVENT
, OLD_TEXT_INSERTED NON_USER_EVENT
},
1445 {OLD_TEXT_REMOVED
, OLD_TEXT_INSERTED
}};
1447 #define TEXT_INSERTED "text-insert"
1448 #define TEXT_REMOVED "text-remove"
1449 #define NON_USER_DETAIL "::system"
1450 static const char* textChangedStrings
[2][2] = {
1451 {TEXT_REMOVED NON_USER_DETAIL
, TEXT_INSERTED NON_USER_DETAIL
},
1452 {TEXT_REMOVED
, TEXT_INSERTED
}};
1454 void MaiAtkObject::FireTextChangeEvent(const nsString
& aStr
, int32_t aStart
,
1455 uint32_t aLen
, bool aIsInsert
,
1457 if (gAvailableAtkSignals
== eUnknown
) {
1458 gAvailableAtkSignals
= g_signal_lookup("text-insert", G_OBJECT_TYPE(this))
1459 ? eHaveNewAtkTextSignals
1463 if (gAvailableAtkSignals
== eNoNewAtkSignals
) {
1464 // XXX remove this code and the gHaveNewTextSignals check when we can
1465 // stop supporting old atk since it doesn't really work anyway
1467 const char* signal_name
= oldTextChangeStrings
[aFromUser
][aIsInsert
];
1468 g_signal_emit_by_name(this, signal_name
, aStart
, aLen
);
1470 const char* signal_name
= textChangedStrings
[aFromUser
][aIsInsert
];
1471 g_signal_emit_by_name(this, signal_name
, aStart
, aLen
,
1472 NS_ConvertUTF16toUTF8(aStr
).get());
1476 void a11y::ProxyShowHideEvent(RemoteAccessible
* aTarget
,
1477 RemoteAccessible
* aParent
, bool aInsert
,
1479 MaiAtkObject
* obj
= MAI_ATK_OBJECT(GetWrapperFor(aTarget
));
1480 obj
->FireAtkShowHideEvent(GetWrapperFor(aParent
), aInsert
, aFromUser
);
1483 #define ADD_EVENT "children_changed::add"
1484 #define HIDE_EVENT "children_changed::remove"
1486 static const char* kMutationStrings
[2][2] = {
1487 {HIDE_EVENT NON_USER_EVENT
, ADD_EVENT NON_USER_EVENT
},
1488 {HIDE_EVENT
, ADD_EVENT
},
1491 void MaiAtkObject::FireAtkShowHideEvent(AtkObject
* aParent
, bool aIsAdded
,
1493 int32_t indexInParent
= getIndexInParentCB(&this->parent
);
1494 const char* signal_name
= kMutationStrings
[aFromUser
][aIsAdded
];
1495 g_signal_emit_by_name(aParent
, signal_name
, indexInParent
, this, nullptr);
1498 void a11y::ProxySelectionEvent(RemoteAccessible
*, RemoteAccessible
* aWidget
,
1500 MaiAtkObject
* obj
= MAI_ATK_OBJECT(GetWrapperFor(aWidget
));
1501 g_signal_emit_by_name(obj
, "selection_changed");
1505 void AccessibleWrap::GetKeyBinding(LocalAccessible
* aAccessible
,
1506 nsAString
& aResult
) {
1507 // Return all key bindings including access key and keyboard shortcut.
1510 nsAutoString keyBindingsStr
;
1511 KeyBinding keyBinding
= aAccessible
->AccessKey();
1512 if (!keyBinding
.IsEmpty()) {
1513 keyBinding
.AppendToString(keyBindingsStr
, KeyBinding::eAtkFormat
);
1515 LocalAccessible
* parent
= aAccessible
->LocalParent();
1516 roles::Role role
= parent
? parent
->Role() : roles::NOTHING
;
1517 if (role
== roles::PARENT_MENUITEM
|| role
== roles::MENUITEM
||
1518 role
== roles::RADIO_MENU_ITEM
|| role
== roles::CHECK_MENU_ITEM
) {
1519 // It is submenu, expose keyboard shortcuts from menu hierarchy like
1521 nsAutoString keysInHierarchyStr
= keyBindingsStr
;
1523 KeyBinding parentKeyBinding
= parent
->AccessKey();
1524 if (!parentKeyBinding
.IsEmpty()) {
1526 parentKeyBinding
.ToString(str
, KeyBinding::eAtkFormat
);
1529 keysInHierarchyStr
.Insert(str
, 0);
1531 } while ((parent
= parent
->LocalParent()) &&
1532 parent
->Role() != roles::MENUBAR
);
1534 keyBindingsStr
.Append(';');
1535 keyBindingsStr
.Append(keysInHierarchyStr
);
1538 // No access key, add ';' to point this.
1539 keyBindingsStr
.Append(';');
1542 // Get keyboard shortcut.
1543 keyBindingsStr
.Append(';');
1544 keyBinding
= aAccessible
->KeyboardShortcut();
1545 if (!keyBinding
.IsEmpty()) {
1546 keyBinding
.AppendToString(keyBindingsStr
, KeyBinding::eAtkFormat
);
1548 aResult
= keyBindingsStr
;
1552 LocalAccessible
* AccessibleWrap::GetColumnHeader(TableAccessible
* aAccessible
,
1558 LocalAccessible
* cell
= aAccessible
->CellAt(0, aColIdx
);
1563 // If the cell at the first row is column header then assume it is column
1564 // header for all rows,
1565 if (cell
->Role() == roles::COLUMNHEADER
) {
1569 // otherwise get column header for the data cell at the first row.
1570 TableCellAccessible
* tableCell
= cell
->AsTableCell();
1575 AutoTArray
<LocalAccessible
*, 10> headerCells
;
1576 tableCell
->ColHeaderCells(&headerCells
);
1577 if (headerCells
.IsEmpty()) {
1581 return headerCells
[0];
1585 LocalAccessible
* AccessibleWrap::GetRowHeader(TableAccessible
* aAccessible
,
1591 LocalAccessible
* cell
= aAccessible
->CellAt(aRowIdx
, 0);
1596 // If the cell at the first column is row header then assume it is row
1597 // header for all columns,
1598 if (cell
->Role() == roles::ROWHEADER
) {
1602 // otherwise get row header for the data cell at the first column.
1603 TableCellAccessible
* tableCell
= cell
->AsTableCell();
1608 AutoTArray
<LocalAccessible
*, 10> headerCells
;
1609 tableCell
->RowHeaderCells(&headerCells
);
1610 if (headerCells
.IsEmpty()) {
1614 return headerCells
[0];