michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "AccessibleWrap.h" michael@0: michael@0: #include "Accessible-inl.h" michael@0: #include "ApplicationAccessibleWrap.h" michael@0: #include "InterfaceInitFuncs.h" michael@0: #include "nsAccUtils.h" michael@0: #include "nsIAccessibleRelation.h" michael@0: #include "nsIAccessibleTable.h" michael@0: #include "RootAccessible.h" michael@0: #include "nsIAccessibleValue.h" michael@0: #include "nsMai.h" michael@0: #include "nsMaiHyperlink.h" michael@0: #include "nsString.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "prprf.h" michael@0: #include "nsStateMap.h" michael@0: #include "Relation.h" michael@0: #include "RootAccessible.h" michael@0: #include "States.h" michael@0: #include "nsISimpleEnumerator.h" michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "nsXPCOMStrings.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsIPersistentProperties2.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::a11y; michael@0: michael@0: AccessibleWrap::EAvailableAtkSignals AccessibleWrap::gAvailableAtkSignals = michael@0: eUnknown; michael@0: michael@0: //defined in ApplicationAccessibleWrap.cpp michael@0: extern "C" GType g_atk_hyperlink_impl_type; michael@0: michael@0: /* MaiAtkObject */ michael@0: michael@0: enum { michael@0: ACTIVATE, michael@0: CREATE, michael@0: DEACTIVATE, michael@0: DESTROY, michael@0: MAXIMIZE, michael@0: MINIMIZE, michael@0: RESIZE, michael@0: RESTORE, michael@0: LAST_SIGNAL michael@0: }; michael@0: michael@0: enum MaiInterfaceType { michael@0: MAI_INTERFACE_COMPONENT, /* 0 */ michael@0: MAI_INTERFACE_ACTION, michael@0: MAI_INTERFACE_VALUE, michael@0: MAI_INTERFACE_EDITABLE_TEXT, michael@0: MAI_INTERFACE_HYPERTEXT, michael@0: MAI_INTERFACE_HYPERLINK_IMPL, michael@0: MAI_INTERFACE_SELECTION, michael@0: MAI_INTERFACE_TABLE, michael@0: MAI_INTERFACE_TEXT, michael@0: MAI_INTERFACE_DOCUMENT, michael@0: MAI_INTERFACE_IMAGE /* 10 */ michael@0: }; michael@0: michael@0: static GType GetAtkTypeForMai(MaiInterfaceType type) michael@0: { michael@0: switch (type) { michael@0: case MAI_INTERFACE_COMPONENT: michael@0: return ATK_TYPE_COMPONENT; michael@0: case MAI_INTERFACE_ACTION: michael@0: return ATK_TYPE_ACTION; michael@0: case MAI_INTERFACE_VALUE: michael@0: return ATK_TYPE_VALUE; michael@0: case MAI_INTERFACE_EDITABLE_TEXT: michael@0: return ATK_TYPE_EDITABLE_TEXT; michael@0: case MAI_INTERFACE_HYPERTEXT: michael@0: return ATK_TYPE_HYPERTEXT; michael@0: case MAI_INTERFACE_HYPERLINK_IMPL: michael@0: return g_atk_hyperlink_impl_type; michael@0: case MAI_INTERFACE_SELECTION: michael@0: return ATK_TYPE_SELECTION; michael@0: case MAI_INTERFACE_TABLE: michael@0: return ATK_TYPE_TABLE; michael@0: case MAI_INTERFACE_TEXT: michael@0: return ATK_TYPE_TEXT; michael@0: case MAI_INTERFACE_DOCUMENT: michael@0: return ATK_TYPE_DOCUMENT; michael@0: case MAI_INTERFACE_IMAGE: michael@0: return ATK_TYPE_IMAGE; michael@0: } michael@0: return G_TYPE_INVALID; michael@0: } michael@0: michael@0: static const char* kNonUserInputEvent = ":system"; michael@0: michael@0: static const GInterfaceInfo atk_if_infos[] = { michael@0: {(GInterfaceInitFunc)componentInterfaceInitCB, michael@0: (GInterfaceFinalizeFunc) nullptr, nullptr}, michael@0: {(GInterfaceInitFunc)actionInterfaceInitCB, michael@0: (GInterfaceFinalizeFunc) nullptr, nullptr}, michael@0: {(GInterfaceInitFunc)valueInterfaceInitCB, michael@0: (GInterfaceFinalizeFunc) nullptr, nullptr}, michael@0: {(GInterfaceInitFunc)editableTextInterfaceInitCB, michael@0: (GInterfaceFinalizeFunc) nullptr, nullptr}, michael@0: {(GInterfaceInitFunc)hypertextInterfaceInitCB, michael@0: (GInterfaceFinalizeFunc) nullptr, nullptr}, michael@0: {(GInterfaceInitFunc)hyperlinkImplInterfaceInitCB, michael@0: (GInterfaceFinalizeFunc) nullptr, nullptr}, michael@0: {(GInterfaceInitFunc)selectionInterfaceInitCB, michael@0: (GInterfaceFinalizeFunc) nullptr, nullptr}, michael@0: {(GInterfaceInitFunc)tableInterfaceInitCB, michael@0: (GInterfaceFinalizeFunc) nullptr, nullptr}, michael@0: {(GInterfaceInitFunc)textInterfaceInitCB, michael@0: (GInterfaceFinalizeFunc) nullptr, nullptr}, michael@0: {(GInterfaceInitFunc)documentInterfaceInitCB, michael@0: (GInterfaceFinalizeFunc) nullptr, nullptr}, michael@0: {(GInterfaceInitFunc)imageInterfaceInitCB, michael@0: (GInterfaceFinalizeFunc) nullptr, nullptr} michael@0: }; michael@0: michael@0: /** michael@0: * This MaiAtkObject is a thin wrapper, in the MAI namespace, for AtkObject michael@0: */ michael@0: struct MaiAtkObject michael@0: { michael@0: AtkObject parent; michael@0: /* michael@0: * The AccessibleWrap whose properties and features are exported michael@0: * via this object instance. michael@0: */ michael@0: AccessibleWrap* accWrap; michael@0: }; michael@0: michael@0: struct MaiAtkObjectClass michael@0: { michael@0: AtkObjectClass parent_class; michael@0: }; michael@0: michael@0: static guint mai_atk_object_signals [LAST_SIGNAL] = { 0, }; michael@0: michael@0: static void MaybeFireNameChange(AtkObject* aAtkObj, const nsString& aNewName); michael@0: michael@0: G_BEGIN_DECLS michael@0: /* callbacks for MaiAtkObject */ michael@0: static void classInitCB(AtkObjectClass *aClass); michael@0: static void initializeCB(AtkObject *aAtkObj, gpointer aData); michael@0: static void finalizeCB(GObject *aObj); michael@0: michael@0: /* callbacks for AtkObject virtual functions */ michael@0: static const gchar* getNameCB (AtkObject *aAtkObj); michael@0: /* getDescriptionCB is also used by image interface */ michael@0: const gchar* getDescriptionCB (AtkObject *aAtkObj); michael@0: static AtkRole getRoleCB(AtkObject *aAtkObj); michael@0: static AtkAttributeSet* getAttributesCB(AtkObject *aAtkObj); michael@0: static const gchar* GetLocaleCB(AtkObject*); michael@0: static AtkObject* getParentCB(AtkObject *aAtkObj); michael@0: static gint getChildCountCB(AtkObject *aAtkObj); michael@0: static AtkObject* refChildCB(AtkObject *aAtkObj, gint aChildIndex); michael@0: static gint getIndexInParentCB(AtkObject *aAtkObj); michael@0: static AtkStateSet* refStateSetCB(AtkObject *aAtkObj); michael@0: static AtkRelationSet* refRelationSetCB(AtkObject *aAtkObj); michael@0: michael@0: /* the missing atkobject virtual functions */ michael@0: /* michael@0: static AtkLayer getLayerCB(AtkObject *aAtkObj); michael@0: static gint getMdiZorderCB(AtkObject *aAtkObj); michael@0: static void SetNameCB(AtkObject *aAtkObj, michael@0: const gchar *name); michael@0: static void SetDescriptionCB(AtkObject *aAtkObj, michael@0: const gchar *description); michael@0: static void SetParentCB(AtkObject *aAtkObj, michael@0: AtkObject *parent); michael@0: static void SetRoleCB(AtkObject *aAtkObj, michael@0: AtkRole role); michael@0: static guint ConnectPropertyChangeHandlerCB( michael@0: AtkObject *aObj, michael@0: AtkPropertyChangeHandler *handler); michael@0: static void RemovePropertyChangeHandlerCB( michael@0: AtkObject *aAtkObj, michael@0: guint handler_id); michael@0: static void InitializeCB(AtkObject *aAtkObj, michael@0: gpointer data); michael@0: static void ChildrenChangedCB(AtkObject *aAtkObj, michael@0: guint change_index, michael@0: gpointer changed_child); michael@0: static void FocusEventCB(AtkObject *aAtkObj, michael@0: gboolean focus_in); michael@0: static void PropertyChangeCB(AtkObject *aAtkObj, michael@0: AtkPropertyValues *values); michael@0: static void StateChangeCB(AtkObject *aAtkObj, michael@0: const gchar *name, michael@0: gboolean state_set); michael@0: static void VisibleDataChangedCB(AtkObject *aAtkObj); michael@0: */ michael@0: G_END_DECLS michael@0: michael@0: static GType GetMaiAtkType(uint16_t interfacesBits); michael@0: static const char * GetUniqueMaiAtkTypeName(uint16_t interfacesBits); michael@0: michael@0: static gpointer parent_class = nullptr; michael@0: michael@0: static GQuark quark_mai_hyperlink = 0; michael@0: michael@0: GType michael@0: mai_atk_object_get_type(void) michael@0: { michael@0: static GType type = 0; michael@0: michael@0: if (!type) { michael@0: static const GTypeInfo tinfo = { michael@0: sizeof(MaiAtkObjectClass), michael@0: (GBaseInitFunc)nullptr, michael@0: (GBaseFinalizeFunc)nullptr, michael@0: (GClassInitFunc)classInitCB, michael@0: (GClassFinalizeFunc)nullptr, michael@0: nullptr, /* class data */ michael@0: sizeof(MaiAtkObject), /* instance size */ michael@0: 0, /* nb preallocs */ michael@0: (GInstanceInitFunc)nullptr, michael@0: nullptr /* value table */ michael@0: }; michael@0: michael@0: type = g_type_register_static(ATK_TYPE_OBJECT, michael@0: "MaiAtkObject", &tinfo, GTypeFlags(0)); michael@0: quark_mai_hyperlink = g_quark_from_static_string("MaiHyperlink"); michael@0: } michael@0: return type; michael@0: } michael@0: michael@0: AccessibleWrap:: michael@0: AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) : michael@0: Accessible(aContent, aDoc), mAtkObject(nullptr) michael@0: { michael@0: } michael@0: michael@0: AccessibleWrap::~AccessibleWrap() michael@0: { michael@0: NS_ASSERTION(!mAtkObject, "ShutdownAtkObject() is not called"); michael@0: } michael@0: michael@0: void michael@0: AccessibleWrap::ShutdownAtkObject() michael@0: { michael@0: if (mAtkObject) { michael@0: if (IS_MAI_OBJECT(mAtkObject)) { michael@0: MAI_ATK_OBJECT(mAtkObject)->accWrap = nullptr; michael@0: } michael@0: SetMaiHyperlink(nullptr); michael@0: g_object_unref(mAtkObject); michael@0: mAtkObject = nullptr; michael@0: } michael@0: } michael@0: michael@0: void michael@0: AccessibleWrap::Shutdown() michael@0: { michael@0: ShutdownAtkObject(); michael@0: Accessible::Shutdown(); michael@0: } michael@0: michael@0: MaiHyperlink* michael@0: AccessibleWrap::GetMaiHyperlink(bool aCreate /* = true */) michael@0: { michael@0: // make sure mAtkObject is created michael@0: GetAtkObject(); michael@0: michael@0: NS_ASSERTION(quark_mai_hyperlink, "quark_mai_hyperlink not initialized"); michael@0: NS_ASSERTION(IS_MAI_OBJECT(mAtkObject), "Invalid AtkObject"); michael@0: MaiHyperlink* maiHyperlink = nullptr; michael@0: if (quark_mai_hyperlink && IS_MAI_OBJECT(mAtkObject)) { michael@0: maiHyperlink = (MaiHyperlink*)g_object_get_qdata(G_OBJECT(mAtkObject), michael@0: quark_mai_hyperlink); michael@0: if (!maiHyperlink && aCreate) { michael@0: maiHyperlink = new MaiHyperlink(this); michael@0: SetMaiHyperlink(maiHyperlink); michael@0: } michael@0: } michael@0: return maiHyperlink; michael@0: } michael@0: michael@0: void michael@0: AccessibleWrap::SetMaiHyperlink(MaiHyperlink* aMaiHyperlink) michael@0: { michael@0: NS_ASSERTION(quark_mai_hyperlink, "quark_mai_hyperlink not initialized"); michael@0: NS_ASSERTION(IS_MAI_OBJECT(mAtkObject), "Invalid AtkObject"); michael@0: if (quark_mai_hyperlink && IS_MAI_OBJECT(mAtkObject)) { michael@0: MaiHyperlink* maiHyperlink = GetMaiHyperlink(false); michael@0: if (!maiHyperlink && !aMaiHyperlink) { michael@0: return; // Never set and we're shutting down michael@0: } michael@0: delete maiHyperlink; michael@0: g_object_set_qdata(G_OBJECT(mAtkObject), quark_mai_hyperlink, michael@0: aMaiHyperlink); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: AccessibleWrap::GetNativeInterface(void** aOutAccessible) michael@0: { michael@0: *aOutAccessible = nullptr; michael@0: michael@0: if (!mAtkObject) { michael@0: if (IsDefunct() || !nsAccUtils::IsEmbeddedObject(this)) { michael@0: // We don't create ATK objects for node which has been shutdown, or michael@0: // nsIAccessible plain text leaves michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: GType type = GetMaiAtkType(CreateMaiInterfaces()); michael@0: NS_ENSURE_TRUE(type, NS_ERROR_FAILURE); michael@0: mAtkObject = michael@0: reinterpret_cast michael@0: (g_object_new(type, nullptr)); michael@0: NS_ENSURE_TRUE(mAtkObject, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: atk_object_initialize(mAtkObject, this); michael@0: mAtkObject->role = ATK_ROLE_INVALID; michael@0: mAtkObject->layer = ATK_LAYER_INVALID; michael@0: } michael@0: michael@0: *aOutAccessible = mAtkObject; michael@0: return NS_OK; michael@0: } michael@0: michael@0: AtkObject * michael@0: AccessibleWrap::GetAtkObject(void) michael@0: { michael@0: void *atkObj = nullptr; michael@0: GetNativeInterface(&atkObj); michael@0: return static_cast(atkObj); michael@0: } michael@0: michael@0: // Get AtkObject from nsIAccessible interface michael@0: /* static */ michael@0: AtkObject * michael@0: AccessibleWrap::GetAtkObject(nsIAccessible* acc) michael@0: { michael@0: void *atkObjPtr = nullptr; michael@0: acc->GetNativeInterface(&atkObjPtr); michael@0: return atkObjPtr ? ATK_OBJECT(atkObjPtr) : nullptr; michael@0: } michael@0: michael@0: /* private */ michael@0: uint16_t michael@0: AccessibleWrap::CreateMaiInterfaces(void) michael@0: { michael@0: uint16_t interfacesBits = 0; michael@0: michael@0: // The Component interface is supported by all accessibles. michael@0: interfacesBits |= 1 << MAI_INTERFACE_COMPONENT; michael@0: michael@0: // Add Action interface if the action count is more than zero. michael@0: if (ActionCount() > 0) michael@0: interfacesBits |= 1 << MAI_INTERFACE_ACTION; michael@0: michael@0: // Text, Editabletext, and Hypertext interface. michael@0: HyperTextAccessible* hyperText = AsHyperText(); michael@0: if (hyperText && hyperText->IsTextRole()) { michael@0: interfacesBits |= 1 << MAI_INTERFACE_TEXT; michael@0: interfacesBits |= 1 << MAI_INTERFACE_EDITABLE_TEXT; michael@0: if (!nsAccUtils::MustPrune(this)) michael@0: interfacesBits |= 1 << MAI_INTERFACE_HYPERTEXT; michael@0: } michael@0: michael@0: // Value interface. michael@0: nsCOMPtr accessInterfaceValue; michael@0: QueryInterface(NS_GET_IID(nsIAccessibleValue), michael@0: getter_AddRefs(accessInterfaceValue)); michael@0: if (accessInterfaceValue) { michael@0: interfacesBits |= 1 << MAI_INTERFACE_VALUE; michael@0: } michael@0: michael@0: // Document interface. michael@0: if (IsDoc()) michael@0: interfacesBits |= 1 << MAI_INTERFACE_DOCUMENT; michael@0: michael@0: if (IsImage()) michael@0: interfacesBits |= 1 << MAI_INTERFACE_IMAGE; michael@0: michael@0: // HyperLink interface. michael@0: if (IsLink()) michael@0: interfacesBits |= 1 << MAI_INTERFACE_HYPERLINK_IMPL; michael@0: michael@0: if (!nsAccUtils::MustPrune(this)) { // These interfaces require children michael@0: // Table interface. michael@0: if (AsTable()) michael@0: interfacesBits |= 1 << MAI_INTERFACE_TABLE; michael@0: michael@0: // Selection interface. michael@0: if (IsSelect()) { michael@0: interfacesBits |= 1 << MAI_INTERFACE_SELECTION; michael@0: } michael@0: } michael@0: michael@0: return interfacesBits; michael@0: } michael@0: michael@0: static GType michael@0: GetMaiAtkType(uint16_t interfacesBits) michael@0: { michael@0: GType type; michael@0: static const GTypeInfo tinfo = { michael@0: sizeof(MaiAtkObjectClass), michael@0: (GBaseInitFunc) nullptr, michael@0: (GBaseFinalizeFunc) nullptr, michael@0: (GClassInitFunc) nullptr, michael@0: (GClassFinalizeFunc) nullptr, michael@0: nullptr, /* class data */ michael@0: sizeof(MaiAtkObject), /* instance size */ michael@0: 0, /* nb preallocs */ michael@0: (GInstanceInitFunc) nullptr, michael@0: nullptr /* value table */ michael@0: }; michael@0: michael@0: /* michael@0: * The members we use to register GTypes are GetAtkTypeForMai michael@0: * and atk_if_infos, which are constant values to each MaiInterface michael@0: * So we can reuse the registered GType when having michael@0: * the same MaiInterface types. michael@0: */ michael@0: const char *atkTypeName = GetUniqueMaiAtkTypeName(interfacesBits); michael@0: type = g_type_from_name(atkTypeName); michael@0: if (type) { michael@0: return type; michael@0: } michael@0: michael@0: /* michael@0: * gobject limits the number of types that can directly derive from any michael@0: * given object type to 4095. michael@0: */ michael@0: static uint16_t typeRegCount = 0; michael@0: if (typeRegCount++ >= 4095) { michael@0: return G_TYPE_INVALID; michael@0: } michael@0: type = g_type_register_static(MAI_TYPE_ATK_OBJECT, michael@0: atkTypeName, michael@0: &tinfo, GTypeFlags(0)); michael@0: michael@0: for (uint32_t index = 0; index < ArrayLength(atk_if_infos); index++) { michael@0: if (interfacesBits & (1 << index)) { michael@0: g_type_add_interface_static(type, michael@0: GetAtkTypeForMai((MaiInterfaceType)index), michael@0: &atk_if_infos[index]); michael@0: } michael@0: } michael@0: michael@0: return type; michael@0: } michael@0: michael@0: static const char* michael@0: GetUniqueMaiAtkTypeName(uint16_t interfacesBits) michael@0: { michael@0: #define MAI_ATK_TYPE_NAME_LEN (30) /* 10+sizeof(uint16_t)*8/4+1 < 30 */ michael@0: michael@0: static gchar namePrefix[] = "MaiAtkType"; /* size = 10 */ michael@0: static gchar name[MAI_ATK_TYPE_NAME_LEN + 1]; michael@0: michael@0: PR_snprintf(name, MAI_ATK_TYPE_NAME_LEN, "%s%x", namePrefix, michael@0: interfacesBits); michael@0: name[MAI_ATK_TYPE_NAME_LEN] = '\0'; michael@0: michael@0: return name; michael@0: } michael@0: michael@0: bool michael@0: AccessibleWrap::IsValidObject() michael@0: { michael@0: // to ensure we are not shut down michael@0: return !IsDefunct(); michael@0: } michael@0: michael@0: /* static functions for ATK callbacks */ michael@0: void michael@0: classInitCB(AtkObjectClass *aClass) michael@0: { michael@0: GObjectClass *gobject_class = G_OBJECT_CLASS(aClass); michael@0: michael@0: parent_class = g_type_class_peek_parent(aClass); michael@0: michael@0: aClass->get_name = getNameCB; michael@0: aClass->get_description = getDescriptionCB; michael@0: aClass->get_parent = getParentCB; michael@0: aClass->get_n_children = getChildCountCB; michael@0: aClass->ref_child = refChildCB; michael@0: aClass->get_index_in_parent = getIndexInParentCB; michael@0: aClass->get_role = getRoleCB; michael@0: aClass->get_attributes = getAttributesCB; michael@0: aClass->get_object_locale = GetLocaleCB; michael@0: aClass->ref_state_set = refStateSetCB; michael@0: aClass->ref_relation_set = refRelationSetCB; michael@0: michael@0: aClass->initialize = initializeCB; michael@0: michael@0: gobject_class->finalize = finalizeCB; michael@0: michael@0: mai_atk_object_signals [ACTIVATE] = michael@0: g_signal_new ("activate", michael@0: MAI_TYPE_ATK_OBJECT, michael@0: G_SIGNAL_RUN_LAST, michael@0: 0, /* default signal handler */ michael@0: nullptr, nullptr, michael@0: g_cclosure_marshal_VOID__VOID, michael@0: G_TYPE_NONE, 0); michael@0: mai_atk_object_signals [CREATE] = michael@0: g_signal_new ("create", michael@0: MAI_TYPE_ATK_OBJECT, michael@0: G_SIGNAL_RUN_LAST, michael@0: 0, /* default signal handler */ michael@0: nullptr, nullptr, michael@0: g_cclosure_marshal_VOID__VOID, michael@0: G_TYPE_NONE, 0); michael@0: mai_atk_object_signals [DEACTIVATE] = michael@0: g_signal_new ("deactivate", michael@0: MAI_TYPE_ATK_OBJECT, michael@0: G_SIGNAL_RUN_LAST, michael@0: 0, /* default signal handler */ michael@0: nullptr, nullptr, michael@0: g_cclosure_marshal_VOID__VOID, michael@0: G_TYPE_NONE, 0); michael@0: mai_atk_object_signals [DESTROY] = michael@0: g_signal_new ("destroy", michael@0: MAI_TYPE_ATK_OBJECT, michael@0: G_SIGNAL_RUN_LAST, michael@0: 0, /* default signal handler */ michael@0: nullptr, nullptr, michael@0: g_cclosure_marshal_VOID__VOID, michael@0: G_TYPE_NONE, 0); michael@0: mai_atk_object_signals [MAXIMIZE] = michael@0: g_signal_new ("maximize", michael@0: MAI_TYPE_ATK_OBJECT, michael@0: G_SIGNAL_RUN_LAST, michael@0: 0, /* default signal handler */ michael@0: nullptr, nullptr, michael@0: g_cclosure_marshal_VOID__VOID, michael@0: G_TYPE_NONE, 0); michael@0: mai_atk_object_signals [MINIMIZE] = michael@0: g_signal_new ("minimize", michael@0: MAI_TYPE_ATK_OBJECT, michael@0: G_SIGNAL_RUN_LAST, michael@0: 0, /* default signal handler */ michael@0: nullptr, nullptr, michael@0: g_cclosure_marshal_VOID__VOID, michael@0: G_TYPE_NONE, 0); michael@0: mai_atk_object_signals [RESIZE] = michael@0: g_signal_new ("resize", michael@0: MAI_TYPE_ATK_OBJECT, michael@0: G_SIGNAL_RUN_LAST, michael@0: 0, /* default signal handler */ michael@0: nullptr, nullptr, michael@0: g_cclosure_marshal_VOID__VOID, michael@0: G_TYPE_NONE, 0); michael@0: mai_atk_object_signals [RESTORE] = michael@0: g_signal_new ("restore", michael@0: MAI_TYPE_ATK_OBJECT, michael@0: G_SIGNAL_RUN_LAST, michael@0: 0, /* default signal handler */ michael@0: nullptr, nullptr, michael@0: g_cclosure_marshal_VOID__VOID, michael@0: G_TYPE_NONE, 0); michael@0: michael@0: } michael@0: michael@0: void michael@0: initializeCB(AtkObject *aAtkObj, gpointer aData) michael@0: { michael@0: NS_ASSERTION((IS_MAI_OBJECT(aAtkObj)), "Invalid AtkObject"); michael@0: NS_ASSERTION(aData, "Invalid Data to init AtkObject"); michael@0: if (!aAtkObj || !aData) michael@0: return; michael@0: michael@0: /* call parent init function */ michael@0: /* AtkObjectClass has not a "initialize" function now, michael@0: * maybe it has later michael@0: */ michael@0: michael@0: if (ATK_OBJECT_CLASS(parent_class)->initialize) michael@0: ATK_OBJECT_CLASS(parent_class)->initialize(aAtkObj, aData); michael@0: michael@0: /* initialize object */ michael@0: MAI_ATK_OBJECT(aAtkObj)->accWrap = michael@0: static_cast(aData); michael@0: } michael@0: michael@0: void michael@0: finalizeCB(GObject *aObj) michael@0: { michael@0: if (!IS_MAI_OBJECT(aObj)) michael@0: return; michael@0: NS_ASSERTION(MAI_ATK_OBJECT(aObj)->accWrap == nullptr, "AccWrap NOT null"); michael@0: michael@0: // call parent finalize function michael@0: // finalize of GObjectClass will unref the accessible parent if has michael@0: if (G_OBJECT_CLASS (parent_class)->finalize) michael@0: G_OBJECT_CLASS (parent_class)->finalize(aObj); michael@0: } michael@0: michael@0: const gchar* michael@0: getNameCB(AtkObject* aAtkObj) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj); michael@0: if (!accWrap) michael@0: return nullptr; michael@0: michael@0: nsAutoString name; michael@0: accWrap->Name(name); michael@0: michael@0: // XXX Firing an event from here does not seem right michael@0: MaybeFireNameChange(aAtkObj, name); michael@0: michael@0: return aAtkObj->name; michael@0: } michael@0: michael@0: static void michael@0: MaybeFireNameChange(AtkObject* aAtkObj, const nsString& aNewName) michael@0: { michael@0: NS_ConvertUTF16toUTF8 newNameUTF8(aNewName); michael@0: if (aAtkObj->name && newNameUTF8.Equals(aAtkObj->name)) michael@0: return; michael@0: michael@0: // Below we duplicate the functionality of atk_object_set_name(), michael@0: // but without calling atk_object_get_name(). Instead of michael@0: // atk_object_get_name() we directly access aAtkObj->name. This is because michael@0: // atk_object_get_name() would call getNameCB() which would call michael@0: // MaybeFireNameChange() (or atk_object_set_name() before this problem was michael@0: // fixed) and we would get an infinite recursion. michael@0: // See http://bugzilla.mozilla.org/733712 michael@0: michael@0: // Do not notify for initial name setting. michael@0: // See bug http://bugzilla.gnome.org/665870 michael@0: bool notify = !!aAtkObj->name; michael@0: michael@0: free(aAtkObj->name); michael@0: aAtkObj->name = strdup(newNameUTF8.get()); michael@0: michael@0: if (notify) michael@0: g_object_notify(G_OBJECT(aAtkObj), "accessible-name"); michael@0: } michael@0: michael@0: const gchar * michael@0: getDescriptionCB(AtkObject *aAtkObj) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj); michael@0: if (!accWrap || accWrap->IsDefunct()) michael@0: return nullptr; michael@0: michael@0: /* nsIAccessible is responsible for the nonnull description */ michael@0: nsAutoString uniDesc; michael@0: accWrap->Description(uniDesc); michael@0: michael@0: NS_ConvertUTF8toUTF16 objDesc(aAtkObj->description); michael@0: if (!uniDesc.Equals(objDesc)) michael@0: atk_object_set_description(aAtkObj, michael@0: NS_ConvertUTF16toUTF8(uniDesc).get()); michael@0: michael@0: return aAtkObj->description; michael@0: } michael@0: michael@0: AtkRole michael@0: getRoleCB(AtkObject *aAtkObj) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj); michael@0: if (!accWrap) michael@0: return ATK_ROLE_INVALID; michael@0: michael@0: #ifdef DEBUG michael@0: NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(accWrap), michael@0: "Does not support nsIAccessibleText when it should"); michael@0: #endif michael@0: michael@0: if (aAtkObj->role != ATK_ROLE_INVALID) michael@0: return aAtkObj->role; michael@0: michael@0: #define ROLE(geckoRole, stringRole, atkRole, macRole, \ michael@0: msaaRole, ia2Role, nameRule) \ michael@0: case roles::geckoRole: \ michael@0: aAtkObj->role = atkRole; \ michael@0: break; michael@0: michael@0: switch (accWrap->Role()) { michael@0: #include "RoleMap.h" michael@0: default: michael@0: MOZ_CRASH("Unknown role."); michael@0: }; michael@0: michael@0: #undef ROLE michael@0: michael@0: if (aAtkObj->role == ATK_ROLE_LIST_BOX && !IsAtkVersionAtLeast(2, 1)) michael@0: aAtkObj->role = ATK_ROLE_LIST; michael@0: else if (aAtkObj->role == ATK_ROLE_TABLE_ROW && !IsAtkVersionAtLeast(2, 1)) michael@0: aAtkObj->role = ATK_ROLE_LIST_ITEM; michael@0: michael@0: return aAtkObj->role; michael@0: } michael@0: michael@0: static AtkAttributeSet* michael@0: ConvertToAtkAttributeSet(nsIPersistentProperties* aAttributes) michael@0: { michael@0: if (!aAttributes) michael@0: return nullptr; michael@0: michael@0: AtkAttributeSet *objAttributeSet = nullptr; michael@0: nsCOMPtr propEnum; michael@0: nsresult rv = aAttributes->Enumerate(getter_AddRefs(propEnum)); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: bool hasMore; michael@0: while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) { michael@0: nsCOMPtr sup; michael@0: rv = propEnum->GetNext(getter_AddRefs(sup)); michael@0: NS_ENSURE_SUCCESS(rv, objAttributeSet); michael@0: michael@0: nsCOMPtr propElem(do_QueryInterface(sup)); michael@0: NS_ENSURE_TRUE(propElem, objAttributeSet); michael@0: michael@0: nsAutoCString name; michael@0: rv = propElem->GetKey(name); michael@0: NS_ENSURE_SUCCESS(rv, objAttributeSet); michael@0: michael@0: nsAutoString value; michael@0: rv = propElem->GetValue(value); michael@0: NS_ENSURE_SUCCESS(rv, objAttributeSet); michael@0: michael@0: AtkAttribute *objAttr = (AtkAttribute *)g_malloc(sizeof(AtkAttribute)); michael@0: objAttr->name = g_strdup(name.get()); michael@0: objAttr->value = g_strdup(NS_ConvertUTF16toUTF8(value).get()); michael@0: objAttributeSet = g_slist_prepend(objAttributeSet, objAttr); michael@0: } michael@0: michael@0: //libspi will free it michael@0: return objAttributeSet; michael@0: } michael@0: michael@0: AtkAttributeSet* michael@0: GetAttributeSet(Accessible* aAccessible) michael@0: { michael@0: nsCOMPtr attributes = aAccessible->Attributes(); michael@0: if (attributes) { michael@0: // There is no ATK state for haspopup, must use object attribute to expose michael@0: // the same info. michael@0: if (aAccessible->State() & states::HASPOPUP) { michael@0: nsAutoString unused; michael@0: attributes->SetStringProperty(NS_LITERAL_CSTRING("haspopup"), michael@0: NS_LITERAL_STRING("true"), unused); michael@0: } michael@0: michael@0: return ConvertToAtkAttributeSet(attributes); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: AtkAttributeSet * michael@0: getAttributesCB(AtkObject *aAtkObj) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj); michael@0: return accWrap ? GetAttributeSet(accWrap) : nullptr; michael@0: } michael@0: michael@0: const gchar* michael@0: GetLocaleCB(AtkObject* aAtkObj) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj); michael@0: if (!accWrap) michael@0: return nullptr; michael@0: michael@0: nsAutoString locale; michael@0: accWrap->Language(locale); michael@0: return AccessibleWrap::ReturnString(locale); michael@0: } michael@0: michael@0: AtkObject * michael@0: getParentCB(AtkObject *aAtkObj) michael@0: { michael@0: if (!aAtkObj->accessible_parent) { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj); michael@0: if (!accWrap) michael@0: return nullptr; michael@0: michael@0: Accessible* accParent = accWrap->Parent(); michael@0: if (!accParent) michael@0: return nullptr; michael@0: michael@0: AtkObject* parent = AccessibleWrap::GetAtkObject(accParent); michael@0: if (parent) michael@0: atk_object_set_parent(aAtkObj, parent); michael@0: } michael@0: return aAtkObj->accessible_parent; michael@0: } michael@0: michael@0: gint michael@0: getChildCountCB(AtkObject *aAtkObj) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj); michael@0: if (!accWrap || nsAccUtils::MustPrune(accWrap)) { michael@0: return 0; michael@0: } michael@0: michael@0: return static_cast(accWrap->EmbeddedChildCount()); michael@0: } michael@0: michael@0: AtkObject * michael@0: refChildCB(AtkObject *aAtkObj, gint aChildIndex) michael@0: { michael@0: // aChildIndex should not be less than zero michael@0: if (aChildIndex < 0) { michael@0: return nullptr; michael@0: } michael@0: michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj); michael@0: if (!accWrap || nsAccUtils::MustPrune(accWrap)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: Accessible* accChild = accWrap->GetEmbeddedChildAt(aChildIndex); michael@0: if (!accChild) michael@0: return nullptr; michael@0: michael@0: AtkObject* childAtkObj = AccessibleWrap::GetAtkObject(accChild); michael@0: michael@0: NS_ASSERTION(childAtkObj, "Fail to get AtkObj"); michael@0: if (!childAtkObj) michael@0: return nullptr; michael@0: g_object_ref(childAtkObj); michael@0: michael@0: if (aAtkObj != childAtkObj->accessible_parent) michael@0: atk_object_set_parent(childAtkObj, aAtkObj); michael@0: michael@0: return childAtkObj; michael@0: } michael@0: michael@0: gint michael@0: getIndexInParentCB(AtkObject *aAtkObj) michael@0: { michael@0: // We don't use nsIAccessible::GetIndexInParent() because michael@0: // for ATK we don't want to include text leaf nodes as children michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj); michael@0: if (!accWrap) { michael@0: return -1; michael@0: } michael@0: michael@0: Accessible* parent = accWrap->Parent(); michael@0: if (!parent) michael@0: return -1; // No parent michael@0: michael@0: return parent->GetIndexOfEmbeddedChild(accWrap); michael@0: } michael@0: michael@0: static void michael@0: TranslateStates(uint64_t aState, AtkStateSet* aStateSet) michael@0: { michael@0: // atk doesn't have a read only state so read only things shouldn't be michael@0: // editable. michael@0: if (aState & states::READONLY) michael@0: aState &= ~states::EDITABLE; michael@0: michael@0: // Convert every state to an entry in AtkStateMap michael@0: uint32_t stateIndex = 0; michael@0: uint64_t bitMask = 1; michael@0: while (gAtkStateMap[stateIndex].stateMapEntryType != kNoSuchState) { michael@0: if (gAtkStateMap[stateIndex].atkState) { // There's potentially an ATK state for this michael@0: bool isStateOn = (aState & bitMask) != 0; michael@0: if (gAtkStateMap[stateIndex].stateMapEntryType == kMapOpposite) { michael@0: isStateOn = !isStateOn; michael@0: } michael@0: if (isStateOn) { michael@0: atk_state_set_add_state(aStateSet, gAtkStateMap[stateIndex].atkState); michael@0: } michael@0: } michael@0: bitMask <<= 1; michael@0: ++ stateIndex; michael@0: } michael@0: } michael@0: michael@0: AtkStateSet * michael@0: refStateSetCB(AtkObject *aAtkObj) michael@0: { michael@0: AtkStateSet *state_set = nullptr; michael@0: state_set = ATK_OBJECT_CLASS(parent_class)->ref_state_set(aAtkObj); michael@0: michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj); michael@0: if (!accWrap) { michael@0: TranslateStates(states::DEFUNCT, state_set); michael@0: return state_set; michael@0: } michael@0: michael@0: // Map states michael@0: TranslateStates(accWrap->State(), state_set); michael@0: michael@0: return state_set; michael@0: } michael@0: michael@0: static void michael@0: UpdateAtkRelation(RelationType aType, Accessible* aAcc, michael@0: AtkRelationType aAtkType, AtkRelationSet* aAtkSet) michael@0: { michael@0: if (aAtkType == ATK_RELATION_NULL) michael@0: return; michael@0: michael@0: AtkRelation* atkRelation = michael@0: atk_relation_set_get_relation_by_type(aAtkSet, aAtkType); michael@0: if (atkRelation) michael@0: atk_relation_set_remove(aAtkSet, atkRelation); michael@0: michael@0: Relation rel(aAcc->RelationByType(aType)); michael@0: nsTArray targets; michael@0: Accessible* tempAcc = nullptr; michael@0: while ((tempAcc = rel.Next())) michael@0: targets.AppendElement(AccessibleWrap::GetAtkObject(tempAcc)); michael@0: michael@0: if (targets.Length()) { michael@0: atkRelation = atk_relation_new(targets.Elements(), michael@0: targets.Length(), aAtkType); michael@0: atk_relation_set_add(aAtkSet, atkRelation); michael@0: g_object_unref(atkRelation); michael@0: } michael@0: } michael@0: michael@0: AtkRelationSet * michael@0: refRelationSetCB(AtkObject *aAtkObj) michael@0: { michael@0: AtkRelationSet* relation_set = michael@0: ATK_OBJECT_CLASS(parent_class)->ref_relation_set(aAtkObj); michael@0: michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj); michael@0: if (!accWrap) michael@0: return relation_set; michael@0: michael@0: #define RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) \ michael@0: UpdateAtkRelation(RelationType::geckoType, accWrap, atkType, relation_set); michael@0: michael@0: #include "RelationTypeMap.h" michael@0: michael@0: #undef RELATIONTYPE michael@0: michael@0: return relation_set; michael@0: } michael@0: michael@0: // Check if aAtkObj is a valid MaiAtkObject, and return the AccessibleWrap michael@0: // for it. michael@0: AccessibleWrap* michael@0: GetAccessibleWrap(AtkObject* aAtkObj) michael@0: { michael@0: NS_ENSURE_TRUE(IS_MAI_OBJECT(aAtkObj), nullptr); michael@0: AccessibleWrap* accWrap = MAI_ATK_OBJECT(aAtkObj)->accWrap; michael@0: michael@0: // Check if the accessible was deconstructed. michael@0: if (!accWrap) michael@0: return nullptr; michael@0: michael@0: NS_ENSURE_TRUE(accWrap->GetAtkObject() == aAtkObj, nullptr); michael@0: michael@0: AccessibleWrap* appAccWrap = ApplicationAcc(); michael@0: if (appAccWrap != accWrap && !accWrap->IsValidObject()) michael@0: return nullptr; michael@0: michael@0: return accWrap; michael@0: } michael@0: michael@0: nsresult michael@0: AccessibleWrap::HandleAccEvent(AccEvent* aEvent) michael@0: { michael@0: nsresult rv = Accessible::HandleAccEvent(aEvent); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: Accessible* accessible = aEvent->GetAccessible(); michael@0: NS_ENSURE_TRUE(accessible, NS_ERROR_FAILURE); michael@0: michael@0: // The accessible can become defunct if we have an xpcom event listener michael@0: // which decides it would be fun to change the DOM and flush layout. michael@0: if (accessible->IsDefunct()) michael@0: return NS_OK; michael@0: michael@0: uint32_t type = aEvent->GetEventType(); michael@0: michael@0: AtkObject* atkObj = AccessibleWrap::GetAtkObject(accessible); michael@0: michael@0: // We don't create ATK objects for nsIAccessible plain text leaves, michael@0: // just return NS_OK in such case michael@0: if (!atkObj) { michael@0: NS_ASSERTION(type == nsIAccessibleEvent::EVENT_SHOW || michael@0: type == nsIAccessibleEvent::EVENT_HIDE, michael@0: "Event other than SHOW and HIDE fired for plain text leaves"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(atkObj); michael@0: if (!accWrap) { michael@0: return NS_OK; // Node is shut down michael@0: } michael@0: michael@0: switch (type) { michael@0: case nsIAccessibleEvent::EVENT_STATE_CHANGE: michael@0: return FireAtkStateChangeEvent(aEvent, atkObj); michael@0: michael@0: case nsIAccessibleEvent::EVENT_TEXT_REMOVED: michael@0: case nsIAccessibleEvent::EVENT_TEXT_INSERTED: michael@0: return FireAtkTextChangedEvent(aEvent, atkObj); michael@0: michael@0: case nsIAccessibleEvent::EVENT_FOCUS: michael@0: { michael@0: a11y::RootAccessible* rootAccWrap = accWrap->RootAccessible(); michael@0: if (rootAccWrap && rootAccWrap->mActivated) { michael@0: atk_focus_tracker_notify(atkObj); michael@0: // Fire state change event for focus michael@0: atk_object_notify_state_change(atkObj, ATK_STATE_FOCUSED, true); michael@0: return NS_OK; michael@0: } michael@0: } break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_NAME_CHANGE: michael@0: { michael@0: nsAutoString newName; michael@0: accessible->Name(newName); michael@0: michael@0: MaybeFireNameChange(atkObj, newName); michael@0: michael@0: break; michael@0: } michael@0: case nsIAccessibleEvent::EVENT_VALUE_CHANGE: michael@0: { michael@0: nsCOMPtr value(do_QueryObject(accessible)); michael@0: if (value) { // Make sure this is a numeric value michael@0: // Don't fire for MSAA string value changes (e.g. text editing) michael@0: // ATK values are always numeric michael@0: g_object_notify( (GObject*)atkObj, "accessible-value" ); michael@0: } michael@0: } break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_SELECTION: michael@0: case nsIAccessibleEvent::EVENT_SELECTION_ADD: michael@0: case nsIAccessibleEvent::EVENT_SELECTION_REMOVE: michael@0: { michael@0: // XXX: dupe events may be fired michael@0: AccSelChangeEvent* selChangeEvent = downcast_accEvent(aEvent); michael@0: g_signal_emit_by_name(AccessibleWrap::GetAtkObject(selChangeEvent->Widget()), michael@0: "selection_changed"); michael@0: break; michael@0: } michael@0: michael@0: case nsIAccessibleEvent::EVENT_SELECTION_WITHIN: michael@0: { michael@0: g_signal_emit_by_name(atkObj, "selection_changed"); michael@0: break; michael@0: } michael@0: michael@0: case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED: michael@0: g_signal_emit_by_name(atkObj, "text_selection_changed"); michael@0: break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED: michael@0: { michael@0: AccCaretMoveEvent* caretMoveEvent = downcast_accEvent(aEvent); michael@0: NS_ASSERTION(caretMoveEvent, "Event needs event data"); michael@0: if (!caretMoveEvent) michael@0: break; michael@0: michael@0: int32_t caretOffset = caretMoveEvent->GetCaretOffset(); michael@0: g_signal_emit_by_name(atkObj, "text_caret_moved", caretOffset); michael@0: } break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED: michael@0: g_signal_emit_by_name(atkObj, "text-attributes-changed"); michael@0: break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_TABLE_MODEL_CHANGED: michael@0: g_signal_emit_by_name(atkObj, "model_changed"); michael@0: break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_TABLE_ROW_INSERT: michael@0: { michael@0: AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent); michael@0: NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE); michael@0: michael@0: int32_t rowIndex = tableEvent->GetIndex(); michael@0: int32_t numRows = tableEvent->GetCount(); michael@0: michael@0: g_signal_emit_by_name(atkObj, "row_inserted", rowIndex, numRows); michael@0: } break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_TABLE_ROW_DELETE: michael@0: { michael@0: AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent); michael@0: NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE); michael@0: michael@0: int32_t rowIndex = tableEvent->GetIndex(); michael@0: int32_t numRows = tableEvent->GetCount(); michael@0: michael@0: g_signal_emit_by_name(atkObj, "row_deleted", rowIndex, numRows); michael@0: } break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_TABLE_ROW_REORDER: michael@0: { michael@0: g_signal_emit_by_name(atkObj, "row_reordered"); michael@0: break; michael@0: } michael@0: michael@0: case nsIAccessibleEvent::EVENT_TABLE_COLUMN_INSERT: michael@0: { michael@0: AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent); michael@0: NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE); michael@0: michael@0: int32_t colIndex = tableEvent->GetIndex(); michael@0: int32_t numCols = tableEvent->GetCount(); michael@0: g_signal_emit_by_name(atkObj, "column_inserted", colIndex, numCols); michael@0: } break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_TABLE_COLUMN_DELETE: michael@0: { michael@0: AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent); michael@0: NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE); michael@0: michael@0: int32_t colIndex = tableEvent->GetIndex(); michael@0: int32_t numCols = tableEvent->GetCount(); michael@0: g_signal_emit_by_name(atkObj, "column_deleted", colIndex, numCols); michael@0: } break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_TABLE_COLUMN_REORDER: michael@0: g_signal_emit_by_name(atkObj, "column_reordered"); michael@0: break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_SECTION_CHANGED: michael@0: g_signal_emit_by_name(atkObj, "visible_data_changed"); michael@0: break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_SHOW: michael@0: return FireAtkShowHideEvent(aEvent, atkObj, true); michael@0: michael@0: case nsIAccessibleEvent::EVENT_HIDE: michael@0: // XXX - Handle native dialog accessibles. michael@0: if (!accessible->IsRoot() && accessible->HasARIARole() && michael@0: accessible->ARIARole() == roles::DIALOG) { michael@0: guint id = g_signal_lookup("deactivate", MAI_TYPE_ATK_OBJECT); michael@0: g_signal_emit(atkObj, id, 0); michael@0: } michael@0: return FireAtkShowHideEvent(aEvent, atkObj, false); michael@0: michael@0: /* michael@0: * Because dealing with menu is very different between nsIAccessible michael@0: * and ATK, and the menu activity is important, specially transfer the michael@0: * following two event. michael@0: * Need more verification by AT test. michael@0: */ michael@0: case nsIAccessibleEvent::EVENT_MENU_START: michael@0: case nsIAccessibleEvent::EVENT_MENU_END: michael@0: break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_WINDOW_ACTIVATE: michael@0: { michael@0: accessible->AsRoot()->mActivated = true; michael@0: guint id = g_signal_lookup("activate", MAI_TYPE_ATK_OBJECT); michael@0: g_signal_emit(atkObj, id, 0); michael@0: michael@0: // Always fire a current focus event after activation. michael@0: FocusMgr()->ForceFocusEvent(); michael@0: } break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE: michael@0: { michael@0: accessible->AsRoot()->mActivated = false; michael@0: guint id = g_signal_lookup("deactivate", MAI_TYPE_ATK_OBJECT); michael@0: g_signal_emit(atkObj, id, 0); michael@0: } break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_WINDOW_MAXIMIZE: michael@0: { michael@0: guint id = g_signal_lookup("maximize", MAI_TYPE_ATK_OBJECT); michael@0: g_signal_emit(atkObj, id, 0); michael@0: } break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE: michael@0: { michael@0: guint id = g_signal_lookup("minimize", MAI_TYPE_ATK_OBJECT); michael@0: g_signal_emit(atkObj, id, 0); michael@0: } break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_WINDOW_RESTORE: michael@0: { michael@0: guint id = g_signal_lookup("restore", MAI_TYPE_ATK_OBJECT); michael@0: g_signal_emit(atkObj, id, 0); michael@0: } break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE: michael@0: g_signal_emit_by_name (atkObj, "load_complete"); michael@0: // XXX - Handle native dialog accessibles. michael@0: if (!accessible->IsRoot() && accessible->HasARIARole() && michael@0: accessible->ARIARole() == roles::DIALOG) { michael@0: guint id = g_signal_lookup("activate", MAI_TYPE_ATK_OBJECT); michael@0: g_signal_emit(atkObj, id, 0); michael@0: } michael@0: break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD: michael@0: g_signal_emit_by_name (atkObj, "reload"); michael@0: break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED: michael@0: g_signal_emit_by_name (atkObj, "load_stopped"); michael@0: break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_MENUPOPUP_START: michael@0: atk_focus_tracker_notify(atkObj); // fire extra focus event michael@0: atk_object_notify_state_change(atkObj, ATK_STATE_VISIBLE, true); michael@0: atk_object_notify_state_change(atkObj, ATK_STATE_SHOWING, true); michael@0: break; michael@0: michael@0: case nsIAccessibleEvent::EVENT_MENUPOPUP_END: michael@0: atk_object_notify_state_change(atkObj, ATK_STATE_VISIBLE, false); michael@0: atk_object_notify_state_change(atkObj, ATK_STATE_SHOWING, false); michael@0: break; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: AccessibleWrap::FireAtkStateChangeEvent(AccEvent* aEvent, michael@0: AtkObject* aObject) michael@0: { michael@0: AccStateChangeEvent* event = downcast_accEvent(aEvent); michael@0: NS_ENSURE_TRUE(event, NS_ERROR_FAILURE); michael@0: michael@0: bool isEnabled = event->IsStateEnabled(); michael@0: int32_t stateIndex = AtkStateMap::GetStateIndexFor(event->GetState()); michael@0: if (stateIndex >= 0) { michael@0: NS_ASSERTION(gAtkStateMap[stateIndex].stateMapEntryType != kNoSuchState, michael@0: "No such state"); michael@0: michael@0: if (gAtkStateMap[stateIndex].atkState != kNone) { michael@0: NS_ASSERTION(gAtkStateMap[stateIndex].stateMapEntryType != kNoStateChange, michael@0: "State changes should not fired for this state"); michael@0: michael@0: if (gAtkStateMap[stateIndex].stateMapEntryType == kMapOpposite) michael@0: isEnabled = !isEnabled; michael@0: michael@0: // Fire state change for first state if there is one to map michael@0: atk_object_notify_state_change(aObject, michael@0: gAtkStateMap[stateIndex].atkState, michael@0: isEnabled); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: AccessibleWrap::FireAtkTextChangedEvent(AccEvent* aEvent, michael@0: AtkObject* aObject) michael@0: { michael@0: AccTextChangeEvent* event = downcast_accEvent(aEvent); michael@0: NS_ENSURE_TRUE(event, NS_ERROR_FAILURE); michael@0: michael@0: int32_t start = event->GetStartOffset(); michael@0: uint32_t length = event->GetLength(); michael@0: bool isInserted = event->IsTextInserted(); michael@0: bool isFromUserInput = aEvent->IsFromUserInput(); michael@0: char* signal_name = nullptr; michael@0: michael@0: if (gAvailableAtkSignals == eUnknown) michael@0: gAvailableAtkSignals = michael@0: g_signal_lookup("text-insert", G_OBJECT_TYPE(aObject)) ? michael@0: eHaveNewAtkTextSignals : eNoNewAtkSignals; michael@0: michael@0: if (gAvailableAtkSignals == eNoNewAtkSignals) { michael@0: // XXX remove this code and the gHaveNewTextSignals check when we can michael@0: // stop supporting old atk since it doesn't really work anyway michael@0: // see bug 619002 michael@0: signal_name = g_strconcat(isInserted ? "text_changed::insert" : michael@0: "text_changed::delete", michael@0: isFromUserInput ? "" : kNonUserInputEvent, nullptr); michael@0: g_signal_emit_by_name(aObject, signal_name, start, length); michael@0: } else { michael@0: nsAutoString text; michael@0: event->GetModifiedText(text); michael@0: signal_name = g_strconcat(isInserted ? "text-insert" : "text-remove", michael@0: isFromUserInput ? "" : "::system", nullptr); michael@0: g_signal_emit_by_name(aObject, signal_name, start, length, michael@0: NS_ConvertUTF16toUTF8(text).get()); michael@0: } michael@0: michael@0: g_free(signal_name); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: AccessibleWrap::FireAtkShowHideEvent(AccEvent* aEvent, michael@0: AtkObject* aObject, bool aIsAdded) michael@0: { michael@0: int32_t indexInParent = getIndexInParentCB(aObject); michael@0: AtkObject *parentObject = getParentCB(aObject); michael@0: NS_ENSURE_STATE(parentObject); michael@0: michael@0: bool isFromUserInput = aEvent->IsFromUserInput(); michael@0: char *signal_name = g_strconcat(aIsAdded ? "children_changed::add" : "children_changed::remove", michael@0: isFromUserInput ? "" : kNonUserInputEvent, nullptr); michael@0: g_signal_emit_by_name(parentObject, signal_name, indexInParent, aObject, nullptr); michael@0: g_free(signal_name); michael@0: michael@0: return NS_OK; michael@0: }