summaryrefslogtreecommitdiffstats
path: root/accessible/atk
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /accessible/atk
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'accessible/atk')
-rw-r--r--accessible/atk/AccessibleWrap.cpp1510
-rw-r--r--accessible/atk/AccessibleWrap.h94
-rw-r--r--accessible/atk/ApplicationAccessibleWrap.cpp146
-rw-r--r--accessible/atk/ApplicationAccessibleWrap.h34
-rw-r--r--accessible/atk/DOMtoATK.cpp151
-rw-r--r--accessible/atk/DOMtoATK.h152
-rw-r--r--accessible/atk/DocAccessibleWrap.cpp36
-rw-r--r--accessible/atk/DocAccessibleWrap.h33
-rw-r--r--accessible/atk/HyperTextAccessibleWrap.h20
-rw-r--r--accessible/atk/InterfaceInitFuncs.h43
-rw-r--r--accessible/atk/Platform.cpp271
-rw-r--r--accessible/atk/RootAccessibleWrap.cpp25
-rw-r--r--accessible/atk/RootAccessibleWrap.h32
-rw-r--r--accessible/atk/UtilInterface.cpp347
-rw-r--r--accessible/atk/moz.build66
-rw-r--r--accessible/atk/nsMai.h112
-rw-r--r--accessible/atk/nsMaiHyperlink.cpp216
-rw-r--r--accessible/atk/nsMaiHyperlink.h49
-rw-r--r--accessible/atk/nsMaiInterfaceAction.cpp83
-rw-r--r--accessible/atk/nsMaiInterfaceComponent.cpp152
-rw-r--r--accessible/atk/nsMaiInterfaceDocument.cpp106
-rw-r--r--accessible/atk/nsMaiInterfaceEditableText.cpp100
-rw-r--r--accessible/atk/nsMaiInterfaceHyperlinkImpl.cpp32
-rw-r--r--accessible/atk/nsMaiInterfaceHypertext.cpp63
-rw-r--r--accessible/atk/nsMaiInterfaceImage.cpp61
-rw-r--r--accessible/atk/nsMaiInterfaceSelection.cpp102
-rw-r--r--accessible/atk/nsMaiInterfaceTable.cpp264
-rw-r--r--accessible/atk/nsMaiInterfaceTableCell.cpp148
-rw-r--r--accessible/atk/nsMaiInterfaceText.cpp564
-rw-r--r--accessible/atk/nsMaiInterfaceValue.cpp98
-rw-r--r--accessible/atk/nsStateMap.h116
31 files changed, 5226 insertions, 0 deletions
diff --git a/accessible/atk/AccessibleWrap.cpp b/accessible/atk/AccessibleWrap.cpp
new file mode 100644
index 0000000000..050bec7fcd
--- /dev/null
+++ b/accessible/atk/AccessibleWrap.cpp
@@ -0,0 +1,1510 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AccessibleWrap.h"
+
+#include "LocalAccessible-inl.h"
+#include "AccAttributes.h"
+#include "ApplicationAccessibleWrap.h"
+#include "InterfaceInitFuncs.h"
+#include "nsAccUtils.h"
+#include "mozilla/a11y/PDocAccessible.h"
+#include "OuterDocAccessible.h"
+#include "RemoteAccessible.h"
+#include "DocAccessibleParent.h"
+#include "RootAccessible.h"
+#include "mozilla/a11y/TableAccessible.h"
+#include "mozilla/a11y/TableCellAccessible.h"
+#include "nsMai.h"
+#include "nsMaiHyperlink.h"
+#include "nsString.h"
+#include "nsStateMap.h"
+#include "mozilla/a11y/Platform.h"
+#include "Relation.h"
+#include "RootAccessible.h"
+#include "States.h"
+#include "nsISimpleEnumerator.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Sprintf.h"
+#include "nsAccessibilityService.h"
+#include "nsComponentManagerUtils.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+MaiAtkObject::EAvailableAtkSignals MaiAtkObject::gAvailableAtkSignals =
+ eUnknown;
+
+// defined in ApplicationAccessibleWrap.cpp
+extern "C" GType g_atk_hyperlink_impl_type;
+
+/* MaiAtkObject */
+
+enum {
+ ACTIVATE,
+ CREATE,
+ DEACTIVATE,
+ DESTROY,
+ MAXIMIZE,
+ MINIMIZE,
+ RESIZE,
+ RESTORE,
+ LAST_SIGNAL
+};
+
+enum MaiInterfaceType {
+ MAI_INTERFACE_COMPONENT, /* 0 */
+ MAI_INTERFACE_ACTION,
+ MAI_INTERFACE_VALUE,
+ MAI_INTERFACE_EDITABLE_TEXT,
+ MAI_INTERFACE_HYPERTEXT,
+ MAI_INTERFACE_HYPERLINK_IMPL,
+ MAI_INTERFACE_SELECTION,
+ MAI_INTERFACE_TABLE,
+ MAI_INTERFACE_TEXT,
+ MAI_INTERFACE_DOCUMENT,
+ MAI_INTERFACE_IMAGE, /* 10 */
+ MAI_INTERFACE_TABLE_CELL
+};
+
+static GType GetAtkTypeForMai(MaiInterfaceType type) {
+ switch (type) {
+ case MAI_INTERFACE_COMPONENT:
+ return ATK_TYPE_COMPONENT;
+ case MAI_INTERFACE_ACTION:
+ return ATK_TYPE_ACTION;
+ case MAI_INTERFACE_VALUE:
+ return ATK_TYPE_VALUE;
+ case MAI_INTERFACE_EDITABLE_TEXT:
+ return ATK_TYPE_EDITABLE_TEXT;
+ case MAI_INTERFACE_HYPERTEXT:
+ return ATK_TYPE_HYPERTEXT;
+ case MAI_INTERFACE_HYPERLINK_IMPL:
+ return g_atk_hyperlink_impl_type;
+ case MAI_INTERFACE_SELECTION:
+ return ATK_TYPE_SELECTION;
+ case MAI_INTERFACE_TABLE:
+ return ATK_TYPE_TABLE;
+ case MAI_INTERFACE_TEXT:
+ return ATK_TYPE_TEXT;
+ case MAI_INTERFACE_DOCUMENT:
+ return ATK_TYPE_DOCUMENT;
+ case MAI_INTERFACE_IMAGE:
+ return ATK_TYPE_IMAGE;
+ case MAI_INTERFACE_TABLE_CELL:
+ MOZ_ASSERT(false);
+ }
+ return G_TYPE_INVALID;
+}
+
+#define NON_USER_EVENT ":system"
+
+// The atk interfaces we can expose without checking what version of ATK we are
+// dealing with. At the moment AtkTableCell is the only interface we can't
+// always expose.
+static const GInterfaceInfo atk_if_infos[] = {
+ {(GInterfaceInitFunc)componentInterfaceInitCB,
+ (GInterfaceFinalizeFunc) nullptr, nullptr},
+ {(GInterfaceInitFunc)actionInterfaceInitCB,
+ (GInterfaceFinalizeFunc) nullptr, nullptr},
+ {(GInterfaceInitFunc)valueInterfaceInitCB, (GInterfaceFinalizeFunc) nullptr,
+ nullptr},
+ {(GInterfaceInitFunc)editableTextInterfaceInitCB,
+ (GInterfaceFinalizeFunc) nullptr, nullptr},
+ {(GInterfaceInitFunc)hypertextInterfaceInitCB,
+ (GInterfaceFinalizeFunc) nullptr, nullptr},
+ {(GInterfaceInitFunc)hyperlinkImplInterfaceInitCB,
+ (GInterfaceFinalizeFunc) nullptr, nullptr},
+ {(GInterfaceInitFunc)selectionInterfaceInitCB,
+ (GInterfaceFinalizeFunc) nullptr, nullptr},
+ {(GInterfaceInitFunc)tableInterfaceInitCB, (GInterfaceFinalizeFunc) nullptr,
+ nullptr},
+ {(GInterfaceInitFunc)textInterfaceInitCB, (GInterfaceFinalizeFunc) nullptr,
+ nullptr},
+ {(GInterfaceInitFunc)documentInterfaceInitCB,
+ (GInterfaceFinalizeFunc) nullptr, nullptr},
+ {(GInterfaceInitFunc)imageInterfaceInitCB, (GInterfaceFinalizeFunc) nullptr,
+ nullptr}};
+
+static GQuark quark_mai_hyperlink = 0;
+
+AtkHyperlink* MaiAtkObject::GetAtkHyperlink() {
+ NS_ASSERTION(quark_mai_hyperlink, "quark_mai_hyperlink not initialized");
+ MaiHyperlink* maiHyperlink =
+ (MaiHyperlink*)g_object_get_qdata(G_OBJECT(this), quark_mai_hyperlink);
+ if (!maiHyperlink) {
+ maiHyperlink = new MaiHyperlink(acc);
+ g_object_set_qdata(G_OBJECT(this), quark_mai_hyperlink, maiHyperlink);
+ }
+
+ return maiHyperlink->GetAtkHyperlink();
+}
+
+void MaiAtkObject::Shutdown() {
+ acc = nullptr;
+ MaiHyperlink* maiHyperlink =
+ (MaiHyperlink*)g_object_get_qdata(G_OBJECT(this), quark_mai_hyperlink);
+ if (maiHyperlink) {
+ delete maiHyperlink;
+ g_object_set_qdata(G_OBJECT(this), quark_mai_hyperlink, nullptr);
+ }
+}
+
+struct MaiAtkObjectClass {
+ AtkObjectClass parent_class;
+};
+
+static guint mai_atk_object_signals[LAST_SIGNAL] = {
+ 0,
+};
+
+static void MaybeFireNameChange(AtkObject* aAtkObj, const nsString& aNewName);
+
+G_BEGIN_DECLS
+/* callbacks for MaiAtkObject */
+static void classInitCB(AtkObjectClass* aClass);
+static void initializeCB(AtkObject* aAtkObj, gpointer aData);
+static void finalizeCB(GObject* aObj);
+
+/* callbacks for AtkObject virtual functions */
+static const gchar* getNameCB(AtkObject* aAtkObj);
+/* getDescriptionCB is also used by image interface */
+const gchar* getDescriptionCB(AtkObject* aAtkObj);
+static AtkRole getRoleCB(AtkObject* aAtkObj);
+static AtkAttributeSet* getAttributesCB(AtkObject* aAtkObj);
+static const gchar* GetLocaleCB(AtkObject*);
+static AtkObject* getParentCB(AtkObject* aAtkObj);
+static gint getChildCountCB(AtkObject* aAtkObj);
+static AtkObject* refChildCB(AtkObject* aAtkObj, gint aChildIndex);
+static gint getIndexInParentCB(AtkObject* aAtkObj);
+static AtkStateSet* refStateSetCB(AtkObject* aAtkObj);
+static AtkRelationSet* refRelationSetCB(AtkObject* aAtkObj);
+
+/* the missing atkobject virtual functions */
+/*
+ static AtkLayer getLayerCB(AtkObject *aAtkObj);
+ static gint getMdiZorderCB(AtkObject *aAtkObj);
+ static void SetNameCB(AtkObject *aAtkObj,
+ const gchar *name);
+ static void SetDescriptionCB(AtkObject *aAtkObj,
+ const gchar *description);
+ static void SetParentCB(AtkObject *aAtkObj,
+ AtkObject *parent);
+ static void SetRoleCB(AtkObject *aAtkObj,
+ AtkRole role);
+ static guint ConnectPropertyChangeHandlerCB(
+ AtkObject *aObj,
+ AtkPropertyChangeHandler *handler);
+ static void RemovePropertyChangeHandlerCB(
+ AtkObject *aAtkObj,
+ guint handler_id);
+ static void InitializeCB(AtkObject *aAtkObj,
+ gpointer data);
+ static void ChildrenChangedCB(AtkObject *aAtkObj,
+ guint change_index,
+ gpointer changed_child);
+ static void FocusEventCB(AtkObject *aAtkObj,
+ gboolean focus_in);
+ static void PropertyChangeCB(AtkObject *aAtkObj,
+ AtkPropertyValues *values);
+ static void StateChangeCB(AtkObject *aAtkObj,
+ const gchar *name,
+ gboolean state_set);
+ static void VisibleDataChangedCB(AtkObject *aAtkObj);
+*/
+G_END_DECLS
+
+static GType GetMaiAtkType(uint16_t interfacesBits);
+static const char* GetUniqueMaiAtkTypeName(uint16_t interfacesBits);
+
+static gpointer parent_class = nullptr;
+
+GType mai_atk_object_get_type(void) {
+ static GType type = 0;
+
+ if (!type) {
+ static const GTypeInfo tinfo = {
+ sizeof(MaiAtkObjectClass),
+ (GBaseInitFunc) nullptr,
+ (GBaseFinalizeFunc) nullptr,
+ (GClassInitFunc)classInitCB,
+ (GClassFinalizeFunc) nullptr,
+ nullptr, /* class data */
+ sizeof(MaiAtkObject), /* instance size */
+ 0, /* nb preallocs */
+ (GInstanceInitFunc) nullptr,
+ nullptr /* value table */
+ };
+
+ type = g_type_register_static(ATK_TYPE_OBJECT, "MaiAtkObject", &tinfo,
+ GTypeFlags(0));
+ quark_mai_hyperlink = g_quark_from_static_string("MaiHyperlink");
+ }
+ return type;
+}
+
+AccessibleWrap::AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc)
+ : LocalAccessible(aContent, aDoc), mAtkObject(nullptr) {}
+
+AccessibleWrap::~AccessibleWrap() {
+ NS_ASSERTION(!mAtkObject, "ShutdownAtkObject() is not called");
+}
+
+void AccessibleWrap::ShutdownAtkObject() {
+ if (!mAtkObject) return;
+
+ NS_ASSERTION(IS_MAI_OBJECT(mAtkObject), "wrong type of atk object");
+ if (IS_MAI_OBJECT(mAtkObject)) MAI_ATK_OBJECT(mAtkObject)->Shutdown();
+
+ g_object_unref(mAtkObject);
+ mAtkObject = nullptr;
+}
+
+void AccessibleWrap::Shutdown() {
+ ShutdownAtkObject();
+ LocalAccessible::Shutdown();
+}
+
+void AccessibleWrap::GetNativeInterface(void** aOutAccessible) {
+ *aOutAccessible = nullptr;
+
+ if (!mAtkObject) {
+ if (IsDefunct() || IsText()) {
+ // We don't create ATK objects for node which has been shutdown or
+ // plain text leaves
+ return;
+ }
+
+ GType type = GetMaiAtkType(CreateMaiInterfaces());
+ if (!type) return;
+
+ mAtkObject = reinterpret_cast<AtkObject*>(g_object_new(type, nullptr));
+ if (!mAtkObject) return;
+
+ atk_object_initialize(mAtkObject, static_cast<Accessible*>(this));
+ mAtkObject->role = ATK_ROLE_INVALID;
+ mAtkObject->layer = ATK_LAYER_INVALID;
+ }
+
+ *aOutAccessible = mAtkObject;
+}
+
+AtkObject* AccessibleWrap::GetAtkObject(void) {
+ void* atkObj = nullptr;
+ GetNativeInterface(&atkObj);
+ return static_cast<AtkObject*>(atkObj);
+}
+
+// Get AtkObject from LocalAccessible interface
+/* static */
+AtkObject* AccessibleWrap::GetAtkObject(LocalAccessible* acc) {
+ void* atkObjPtr = nullptr;
+ acc->GetNativeInterface(&atkObjPtr);
+ return atkObjPtr ? ATK_OBJECT(atkObjPtr) : nullptr;
+}
+
+/* private */
+uint16_t AccessibleWrap::CreateMaiInterfaces(void) {
+ uint16_t interfacesBits = 0;
+
+ // The Component interface is supported by all accessibles.
+ interfacesBits |= 1 << MAI_INTERFACE_COMPONENT;
+
+ // Add Action interface if the action count is more than zero.
+ if (ActionCount() > 0) interfacesBits |= 1 << MAI_INTERFACE_ACTION;
+
+ // Text, Editabletext, and Hypertext interface.
+ HyperTextAccessible* hyperText = AsHyperText();
+ if (hyperText && hyperText->IsTextRole()) {
+ interfacesBits |= 1 << MAI_INTERFACE_TEXT;
+ interfacesBits |= 1 << MAI_INTERFACE_EDITABLE_TEXT;
+ if (!nsAccUtils::MustPrune(this)) {
+ interfacesBits |= 1 << MAI_INTERFACE_HYPERTEXT;
+ }
+ }
+
+ // Value interface.
+ if (HasNumericValue()) interfacesBits |= 1 << MAI_INTERFACE_VALUE;
+
+ // Document interface.
+ if (IsDoc()) interfacesBits |= 1 << MAI_INTERFACE_DOCUMENT;
+
+ if (IsImage()) interfacesBits |= 1 << MAI_INTERFACE_IMAGE;
+
+ // HyperLink interface.
+ if (IsLink()) interfacesBits |= 1 << MAI_INTERFACE_HYPERLINK_IMPL;
+
+ if (!nsAccUtils::MustPrune(this)) { // These interfaces require children
+ // Table interface.
+ if (AsTable()) interfacesBits |= 1 << MAI_INTERFACE_TABLE;
+
+ if (AsTableCell()) interfacesBits |= 1 << MAI_INTERFACE_TABLE_CELL;
+
+ // Selection interface.
+ if (IsSelect()) {
+ interfacesBits |= 1 << MAI_INTERFACE_SELECTION;
+ }
+ }
+
+ return interfacesBits;
+}
+
+static GType GetMaiAtkType(uint16_t interfacesBits) {
+ GType type;
+ static const GTypeInfo tinfo = {
+ sizeof(MaiAtkObjectClass),
+ (GBaseInitFunc) nullptr,
+ (GBaseFinalizeFunc) nullptr,
+ (GClassInitFunc) nullptr,
+ (GClassFinalizeFunc) nullptr,
+ nullptr, /* class data */
+ sizeof(MaiAtkObject), /* instance size */
+ 0, /* nb preallocs */
+ (GInstanceInitFunc) nullptr,
+ nullptr /* value table */
+ };
+
+ /*
+ * The members we use to register GTypes are GetAtkTypeForMai
+ * and atk_if_infos, which are constant values to each MaiInterface
+ * So we can reuse the registered GType when having
+ * the same MaiInterface types.
+ */
+ const char* atkTypeName = GetUniqueMaiAtkTypeName(interfacesBits);
+ type = g_type_from_name(atkTypeName);
+ if (type) {
+ return type;
+ }
+
+ /*
+ * gobject limits the number of types that can directly derive from any
+ * given object type to 4095.
+ */
+ static uint16_t typeRegCount = 0;
+ if (typeRegCount++ >= 4095) {
+ return G_TYPE_INVALID;
+ }
+ type = g_type_register_static(MAI_TYPE_ATK_OBJECT, atkTypeName, &tinfo,
+ GTypeFlags(0));
+
+ for (uint32_t index = 0; index < ArrayLength(atk_if_infos); index++) {
+ if (interfacesBits & (1 << index)) {
+ g_type_add_interface_static(type,
+ GetAtkTypeForMai((MaiInterfaceType)index),
+ &atk_if_infos[index]);
+ }
+ }
+
+ // Special case AtkTableCell so we can check what version of Atk we are
+ // dealing with.
+ if (IsAtkVersionAtLeast(2, 12) &&
+ (interfacesBits & (1 << MAI_INTERFACE_TABLE_CELL))) {
+ const GInterfaceInfo cellInfo = {
+ (GInterfaceInitFunc)tableCellInterfaceInitCB,
+ (GInterfaceFinalizeFunc) nullptr, nullptr};
+ g_type_add_interface_static(type, gAtkTableCellGetTypeFunc(), &cellInfo);
+ }
+
+ return type;
+}
+
+static const char* GetUniqueMaiAtkTypeName(uint16_t interfacesBits) {
+#define MAI_ATK_TYPE_NAME_LEN (30) /* 10+sizeof(uint16_t)*8/4+1 < 30 */
+
+ static gchar namePrefix[] = "MaiAtkType"; /* size = 10 */
+ static gchar name[MAI_ATK_TYPE_NAME_LEN + 1];
+
+ SprintfLiteral(name, "%s%x", namePrefix, interfacesBits);
+ name[MAI_ATK_TYPE_NAME_LEN] = '\0';
+
+ return name;
+}
+
+bool AccessibleWrap::IsValidObject() {
+ // to ensure we are not shut down
+ return !IsDefunct();
+}
+
+/* static functions for ATK callbacks */
+void classInitCB(AtkObjectClass* aClass) {
+ GObjectClass* gobject_class = G_OBJECT_CLASS(aClass);
+
+ parent_class = g_type_class_peek_parent(aClass);
+
+ aClass->get_name = getNameCB;
+ aClass->get_description = getDescriptionCB;
+ aClass->get_parent = getParentCB;
+ aClass->get_n_children = getChildCountCB;
+ aClass->ref_child = refChildCB;
+ aClass->get_index_in_parent = getIndexInParentCB;
+ aClass->get_role = getRoleCB;
+ aClass->get_attributes = getAttributesCB;
+ aClass->get_object_locale = GetLocaleCB;
+ aClass->ref_state_set = refStateSetCB;
+ aClass->ref_relation_set = refRelationSetCB;
+
+ aClass->initialize = initializeCB;
+
+ gobject_class->finalize = finalizeCB;
+
+ mai_atk_object_signals[ACTIVATE] = g_signal_new(
+ "activate", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST,
+ 0, /* default signal handler */
+ nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+ mai_atk_object_signals[CREATE] = g_signal_new(
+ "create", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST,
+ 0, /* default signal handler */
+ nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+ mai_atk_object_signals[DEACTIVATE] = g_signal_new(
+ "deactivate", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST,
+ 0, /* default signal handler */
+ nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+ mai_atk_object_signals[DESTROY] = g_signal_new(
+ "destroy", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST,
+ 0, /* default signal handler */
+ nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+ mai_atk_object_signals[MAXIMIZE] = g_signal_new(
+ "maximize", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST,
+ 0, /* default signal handler */
+ nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+ mai_atk_object_signals[MINIMIZE] = g_signal_new(
+ "minimize", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST,
+ 0, /* default signal handler */
+ nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+ mai_atk_object_signals[RESIZE] = g_signal_new(
+ "resize", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST,
+ 0, /* default signal handler */
+ nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+ mai_atk_object_signals[RESTORE] = g_signal_new(
+ "restore", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST,
+ 0, /* default signal handler */
+ nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+}
+
+void initializeCB(AtkObject* aAtkObj, gpointer aData) {
+ NS_ASSERTION((IS_MAI_OBJECT(aAtkObj)), "Invalid AtkObject");
+ NS_ASSERTION(aData, "Invalid Data to init AtkObject");
+ if (!aAtkObj || !aData) return;
+
+ /* call parent init function */
+ /* AtkObjectClass has not a "initialize" function now,
+ * maybe it has later
+ */
+
+ if (ATK_OBJECT_CLASS(parent_class)->initialize) {
+ ATK_OBJECT_CLASS(parent_class)->initialize(aAtkObj, aData);
+ }
+
+ /* initialize object */
+ MAI_ATK_OBJECT(aAtkObj)->acc = static_cast<Accessible*>(aData);
+}
+
+void finalizeCB(GObject* aObj) {
+ if (!IS_MAI_OBJECT(aObj)) return;
+ NS_ASSERTION(!MAI_ATK_OBJECT(aObj)->acc, "acc NOT null");
+
+ // call parent finalize function
+ // finalize of GObjectClass will unref the accessible parent if has
+ if (G_OBJECT_CLASS(parent_class)->finalize) {
+ G_OBJECT_CLASS(parent_class)->finalize(aObj);
+ }
+}
+
+const gchar* getNameCB(AtkObject* aAtkObj) {
+ nsAutoString name;
+ if (Accessible* acc = GetInternalObj(aAtkObj)) {
+ acc->Name(name);
+ } else {
+ return nullptr;
+ }
+
+ // XXX Firing an event from here does not seem right
+ MaybeFireNameChange(aAtkObj, name);
+
+ return aAtkObj->name;
+}
+
+static void MaybeFireNameChange(AtkObject* aAtkObj, const nsString& aNewName) {
+ NS_ConvertUTF16toUTF8 newNameUTF8(aNewName);
+ if (aAtkObj->name && !strcmp(aAtkObj->name, newNameUTF8.get())) return;
+
+ // Below we duplicate the functionality of atk_object_set_name(),
+ // but without calling atk_object_get_name(). Instead of
+ // atk_object_get_name() we directly access aAtkObj->name. This is because
+ // atk_object_get_name() would call getNameCB() which would call
+ // MaybeFireNameChange() (or atk_object_set_name() before this problem was
+ // fixed) and we would get an infinite recursion.
+ // See http://bugzilla.mozilla.org/733712
+
+ // Do not notify for initial name setting.
+ // See bug http://bugzilla.gnome.org/665870
+ bool notify = !!aAtkObj->name;
+
+ free(aAtkObj->name);
+ aAtkObj->name = strdup(newNameUTF8.get());
+
+ if (notify) g_object_notify(G_OBJECT(aAtkObj), "accessible-name");
+}
+
+const gchar* getDescriptionCB(AtkObject* aAtkObj) {
+ nsAutoString uniDesc;
+ if (Accessible* acc = GetInternalObj(aAtkObj)) {
+ acc->Description(uniDesc);
+ } else {
+ return nullptr;
+ }
+
+ NS_ConvertUTF8toUTF16 objDesc(aAtkObj->description);
+ if (!uniDesc.Equals(objDesc)) {
+ atk_object_set_description(aAtkObj, NS_ConvertUTF16toUTF8(uniDesc).get());
+ }
+
+ return aAtkObj->description;
+}
+
+AtkRole getRoleCB(AtkObject* aAtkObj) {
+ if (aAtkObj->role != ATK_ROLE_INVALID) return aAtkObj->role;
+
+ Accessible* acc = GetInternalObj(aAtkObj);
+ if (!acc) {
+ return ATK_ROLE_INVALID;
+ }
+
+#ifdef DEBUG
+ if (AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj)) {
+ NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(accWrap),
+ "Does not support Text interface when it should");
+ }
+#endif
+
+#define ROLE(geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \
+ msaaRole, ia2Role, androidClass, nameRule) \
+ case roles::geckoRole: \
+ aAtkObj->role = atkRole; \
+ break;
+
+ switch (acc->Role()) {
+#include "RoleMap.h"
+ default:
+ MOZ_CRASH("Unknown role.");
+ }
+
+#undef ROLE
+
+ if (aAtkObj->role == ATK_ROLE_LIST_BOX && !IsAtkVersionAtLeast(2, 1)) {
+ aAtkObj->role = ATK_ROLE_LIST;
+ } else if (aAtkObj->role == ATK_ROLE_TABLE_ROW &&
+ !IsAtkVersionAtLeast(2, 1)) {
+ aAtkObj->role = ATK_ROLE_LIST_ITEM;
+ } else if (aAtkObj->role == ATK_ROLE_MATH && !IsAtkVersionAtLeast(2, 12)) {
+ aAtkObj->role = ATK_ROLE_SECTION;
+ } else if (aAtkObj->role == ATK_ROLE_COMMENT && !IsAtkVersionAtLeast(2, 12)) {
+ aAtkObj->role = ATK_ROLE_SECTION;
+ } else if (aAtkObj->role == ATK_ROLE_LANDMARK &&
+ !IsAtkVersionAtLeast(2, 12)) {
+ aAtkObj->role = ATK_ROLE_SECTION;
+ } else if (aAtkObj->role == ATK_ROLE_FOOTNOTE &&
+ !IsAtkVersionAtLeast(2, 25, 2)) {
+ aAtkObj->role = ATK_ROLE_SECTION;
+ } else if (aAtkObj->role == ATK_ROLE_STATIC && !IsAtkVersionAtLeast(2, 16)) {
+ aAtkObj->role = ATK_ROLE_TEXT;
+ } else if ((aAtkObj->role == ATK_ROLE_MATH_FRACTION ||
+ aAtkObj->role == ATK_ROLE_MATH_ROOT) &&
+ !IsAtkVersionAtLeast(2, 16)) {
+ aAtkObj->role = ATK_ROLE_SECTION;
+ } else if (aAtkObj->role == ATK_ROLE_MARK && !IsAtkVersionAtLeast(2, 36)) {
+ aAtkObj->role = ATK_ROLE_TEXT;
+ } else if (aAtkObj->role == ATK_ROLE_SUGGESTION &&
+ !IsAtkVersionAtLeast(2, 36)) {
+ aAtkObj->role = ATK_ROLE_SECTION;
+ } else if (aAtkObj->role == ATK_ROLE_COMMENT && !IsAtkVersionAtLeast(2, 36)) {
+ aAtkObj->role = ATK_ROLE_SECTION;
+ } else if ((aAtkObj->role == ATK_ROLE_CONTENT_DELETION ||
+ aAtkObj->role == ATK_ROLE_CONTENT_INSERTION) &&
+ !IsAtkVersionAtLeast(2, 34)) {
+ aAtkObj->role = ATK_ROLE_SECTION;
+ }
+
+ return aAtkObj->role;
+}
+
+static AtkAttributeSet* ConvertToAtkAttributeSet(AccAttributes* aAttributes) {
+ if (!aAttributes) {
+ return nullptr;
+ }
+
+ AtkAttributeSet* objAttributeSet = nullptr;
+
+ for (auto iter : *aAttributes) {
+ nsAutoString name;
+ iter.NameAsString(name);
+ if (name.Equals(u"placeholder")) {
+ name.AssignLiteral(u"placeholder-text");
+ }
+
+ nsAutoString value;
+ iter.ValueAsString(value);
+
+ AtkAttribute* objAttr = (AtkAttribute*)g_malloc(sizeof(AtkAttribute));
+ objAttr->name = g_strdup(NS_ConvertUTF16toUTF8(name).get());
+ objAttr->value = g_strdup(NS_ConvertUTF16toUTF8(value).get());
+ objAttributeSet = g_slist_prepend(objAttributeSet, objAttr);
+ }
+
+ // libspi will free it
+ return objAttributeSet;
+}
+
+AtkAttributeSet* getAttributesCB(AtkObject* aAtkObj) {
+ Accessible* acc = GetInternalObj(aAtkObj);
+ if (!acc) {
+ return nullptr;
+ }
+ RefPtr<AccAttributes> attributes = acc->Attributes();
+ return ConvertToAtkAttributeSet(attributes);
+}
+
+const gchar* GetLocaleCB(AtkObject* aAtkObj) {
+ Accessible* acc = GetInternalObj(aAtkObj);
+ if (!acc) {
+ return nullptr;
+ }
+
+ nsAutoString locale;
+ acc->Language(locale);
+ return AccessibleWrap::ReturnString(locale);
+}
+
+AtkObject* getParentCB(AtkObject* aAtkObj) {
+ if (aAtkObj->accessible_parent) return aAtkObj->accessible_parent;
+
+ Accessible* acc = GetInternalObj(aAtkObj);
+ if (!acc) {
+ return nullptr;
+ }
+
+ Accessible* parent = acc->Parent();
+ AtkObject* atkParent = parent ? GetWrapperFor(parent) : nullptr;
+ if (atkParent) atk_object_set_parent(aAtkObj, atkParent);
+
+ return aAtkObj->accessible_parent;
+}
+
+gint getChildCountCB(AtkObject* aAtkObj) {
+ Accessible* acc = GetInternalObj(aAtkObj);
+ if (!acc || nsAccUtils::MustPrune(acc)) {
+ return 0;
+ }
+ return static_cast<gint>(acc->EmbeddedChildCount());
+}
+
+AtkObject* refChildCB(AtkObject* aAtkObj, gint aChildIndex) {
+ // aChildIndex should not be less than zero
+ if (aChildIndex < 0) {
+ return nullptr;
+ }
+
+ Accessible* acc = GetInternalObj(aAtkObj);
+ if (!acc || nsAccUtils::MustPrune(acc)) {
+ return nullptr;
+ }
+ Accessible* accChild = acc->EmbeddedChildAt(aChildIndex);
+ if (!accChild) {
+ return nullptr;
+ }
+
+ AtkObject* childAtkObj = GetWrapperFor(accChild);
+ NS_ASSERTION(childAtkObj, "Fail to get AtkObj");
+ if (!childAtkObj) {
+ return nullptr;
+ }
+
+ g_object_ref(childAtkObj);
+
+ if (aAtkObj != childAtkObj->accessible_parent) {
+ atk_object_set_parent(childAtkObj, aAtkObj);
+ }
+
+ return childAtkObj;
+}
+
+gint getIndexInParentCB(AtkObject* aAtkObj) {
+ // We don't use LocalAccessible::IndexInParent() because we don't include text
+ // leaf nodes as children in ATK.
+ Accessible* acc = GetInternalObj(aAtkObj);
+ if (!acc) {
+ return -1;
+ }
+ if (acc->IsDoc()) {
+ return 0;
+ }
+ Accessible* parent = acc->Parent();
+ if (!parent) {
+ return -1;
+ }
+ return parent->IndexOfEmbeddedChild(acc);
+}
+
+static void TranslateStates(uint64_t aState, roles::Role aRole,
+ AtkStateSet* aStateSet) {
+ // atk doesn't have a read only state so read only things shouldn't be
+ // editable. However, we don't do this for list items because Gecko always
+ // exposes those as read only.
+ if ((aState & states::READONLY) && aRole != roles::LISTITEM) {
+ aState &= ~states::EDITABLE;
+ }
+
+ // Convert every state to an entry in AtkStateMap
+ uint64_t bitMask = 1;
+ for (auto stateIndex = 0U; stateIndex < gAtkStateMapLen; stateIndex++) {
+ if (gAtkStateMap[stateIndex]
+ .atkState) { // There's potentially an ATK state for this
+ bool isStateOn = (aState & bitMask) != 0;
+ if (gAtkStateMap[stateIndex].stateMapEntryType == kMapOpposite) {
+ isStateOn = !isStateOn;
+ }
+ if (isStateOn) {
+ atk_state_set_add_state(aStateSet, gAtkStateMap[stateIndex].atkState);
+ }
+ }
+ bitMask <<= 1;
+ }
+}
+
+AtkStateSet* refStateSetCB(AtkObject* aAtkObj) {
+ AtkStateSet* state_set = nullptr;
+ state_set = ATK_OBJECT_CLASS(parent_class)->ref_state_set(aAtkObj);
+
+ if (Accessible* acc = GetInternalObj(aAtkObj)) {
+ TranslateStates(acc->State(), acc->Role(), state_set);
+ } else {
+ TranslateStates(states::DEFUNCT, roles::NOTHING, state_set);
+ }
+
+ return state_set;
+}
+
+static void UpdateAtkRelation(RelationType aType, Accessible* aAcc,
+ AtkRelationType aAtkType,
+ AtkRelationSet* aAtkSet) {
+ if (aAtkType == ATK_RELATION_NULL) return;
+
+ AtkRelation* atkRelation =
+ atk_relation_set_get_relation_by_type(aAtkSet, aAtkType);
+ if (atkRelation) atk_relation_set_remove(aAtkSet, atkRelation);
+
+ Relation rel(aAcc->RelationByType(aType));
+ nsTArray<AtkObject*> targets;
+ Accessible* tempAcc = nullptr;
+ while ((tempAcc = rel.Next())) {
+ targets.AppendElement(GetWrapperFor(tempAcc));
+ }
+
+ if (targets.Length()) {
+ atkRelation =
+ atk_relation_new(targets.Elements(), targets.Length(), aAtkType);
+ atk_relation_set_add(aAtkSet, atkRelation);
+ g_object_unref(atkRelation);
+ }
+}
+
+AtkRelationSet* refRelationSetCB(AtkObject* aAtkObj) {
+ AtkRelationSet* relation_set =
+ ATK_OBJECT_CLASS(parent_class)->ref_relation_set(aAtkObj);
+
+ Accessible* acc = GetInternalObj(aAtkObj);
+ if (!acc) {
+ return relation_set;
+ }
+
+#define RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) \
+ UpdateAtkRelation(RelationType::geckoType, acc, atkType, relation_set);
+
+#include "RelationTypeMap.h"
+
+#undef RELATIONTYPE
+
+ return relation_set;
+}
+
+// Check if aAtkObj is a valid MaiAtkObject, and return the AccessibleWrap
+// for it.
+AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj) {
+ NS_ENSURE_TRUE(IS_MAI_OBJECT(aAtkObj), nullptr);
+
+ // If we're working with an ATK object, we need to convert the Accessible
+ // back to an AccessibleWrap:
+ Accessible* storedAcc = MAI_ATK_OBJECT(aAtkObj)->acc;
+ if (!storedAcc) {
+ return nullptr;
+ }
+ auto* accWrap = static_cast<AccessibleWrap*>(storedAcc->AsLocal());
+
+ // Check if the accessible was deconstructed.
+ if (!accWrap) return nullptr;
+
+ NS_ENSURE_TRUE(accWrap->GetAtkObject() == aAtkObj, nullptr);
+
+ AccessibleWrap* appAccWrap = ApplicationAcc();
+ if (appAccWrap != accWrap && !accWrap->IsValidObject()) {
+ return nullptr;
+ }
+
+ return accWrap;
+}
+
+RemoteAccessible* GetProxy(AtkObject* aObj) {
+ Accessible* acc = GetInternalObj(aObj);
+ if (!acc) {
+ return nullptr;
+ }
+
+ return acc->AsRemote();
+}
+
+Accessible* GetInternalObj(AtkObject* aObj) {
+ if (!aObj || !IS_MAI_OBJECT(aObj)) return nullptr;
+
+ return MAI_ATK_OBJECT(aObj)->acc;
+}
+
+AtkObject* GetWrapperFor(Accessible* aAcc) {
+ if (!aAcc) {
+ return nullptr;
+ }
+
+ if (aAcc->IsRemote()) {
+ return reinterpret_cast<AtkObject*>(aAcc->AsRemote()->GetWrapper());
+ }
+
+ return AccessibleWrap::GetAtkObject(aAcc->AsLocal());
+}
+
+static uint16_t GetInterfacesForProxy(RemoteAccessible* aProxy) {
+ uint16_t interfaces = 1 << MAI_INTERFACE_COMPONENT;
+ if (aProxy->IsHyperText()) {
+ interfaces |= (1 << MAI_INTERFACE_HYPERTEXT) | (1 << MAI_INTERFACE_TEXT) |
+ (1 << MAI_INTERFACE_EDITABLE_TEXT);
+ }
+
+ if (aProxy->IsLink()) {
+ interfaces |= 1 << MAI_INTERFACE_HYPERLINK_IMPL;
+ }
+
+ if (aProxy->HasNumericValue()) {
+ interfaces |= 1 << MAI_INTERFACE_VALUE;
+ }
+
+ if (aProxy->IsTable()) {
+ interfaces |= 1 << MAI_INTERFACE_TABLE;
+ }
+
+ if (aProxy->IsTableCell()) {
+ interfaces |= 1 << MAI_INTERFACE_TABLE_CELL;
+ }
+
+ if (aProxy->IsImage()) {
+ interfaces |= 1 << MAI_INTERFACE_IMAGE;
+ }
+
+ if (aProxy->IsDoc()) {
+ interfaces |= 1 << MAI_INTERFACE_DOCUMENT;
+ }
+
+ if (aProxy->IsSelect()) {
+ interfaces |= 1 << MAI_INTERFACE_SELECTION;
+ }
+
+ if (aProxy->IsActionable()) {
+ interfaces |= 1 << MAI_INTERFACE_ACTION;
+ }
+
+ return interfaces;
+}
+
+void a11y::ProxyCreated(RemoteAccessible* aProxy) {
+ MOZ_ASSERT(aProxy->RemoteParent() || aProxy->IsDoc(),
+ "Need parent to check for HyperLink interface");
+ GType type = GetMaiAtkType(GetInterfacesForProxy(aProxy));
+ NS_ASSERTION(type, "why don't we have a type!");
+
+ AtkObject* obj = reinterpret_cast<AtkObject*>(g_object_new(type, nullptr));
+ if (!obj) return;
+
+ atk_object_initialize(obj, static_cast<Accessible*>(aProxy));
+ obj->role = ATK_ROLE_INVALID;
+ obj->layer = ATK_LAYER_INVALID;
+ aProxy->SetWrapper(reinterpret_cast<uintptr_t>(obj));
+}
+
+void a11y::ProxyDestroyed(RemoteAccessible* aProxy) {
+ auto obj = reinterpret_cast<MaiAtkObject*>(aProxy->GetWrapper());
+ if (!obj) {
+ return;
+ }
+
+ obj->Shutdown();
+ g_object_unref(obj);
+ aProxy->SetWrapper(0);
+}
+
+nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) {
+ nsresult rv = LocalAccessible::HandleAccEvent(aEvent);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (IPCAccessibilityActive()) {
+ return NS_OK;
+ }
+
+ LocalAccessible* accessible = aEvent->GetAccessible();
+ NS_ENSURE_TRUE(accessible, NS_ERROR_FAILURE);
+
+ // The accessible can become defunct if we have an xpcom event listener
+ // which decides it would be fun to change the DOM and flush layout.
+ if (accessible->IsDefunct()) return NS_OK;
+
+ uint32_t type = aEvent->GetEventType();
+
+ AtkObject* atkObj = AccessibleWrap::GetAtkObject(accessible);
+
+ // We don't create ATK objects for plain text leaves, just return NS_OK in
+ // such case.
+ if (!atkObj) {
+ NS_ASSERTION(type == nsIAccessibleEvent::EVENT_SHOW ||
+ type == nsIAccessibleEvent::EVENT_HIDE,
+ "Event other than SHOW and HIDE fired for plain text leaves");
+ return NS_OK;
+ }
+
+ AccessibleWrap* accWrap = GetAccessibleWrap(atkObj);
+ if (!accWrap) {
+ return NS_OK; // Node is shut down
+ }
+
+ switch (type) {
+ case nsIAccessibleEvent::EVENT_STATE_CHANGE: {
+ AccStateChangeEvent* event = downcast_accEvent(aEvent);
+ MAI_ATK_OBJECT(atkObj)->FireStateChangeEvent(event->GetState(),
+ event->IsStateEnabled());
+ break;
+ }
+
+ case nsIAccessibleEvent::EVENT_TEXT_REMOVED:
+ case nsIAccessibleEvent::EVENT_TEXT_INSERTED: {
+ AccTextChangeEvent* event = downcast_accEvent(aEvent);
+ NS_ENSURE_TRUE(event, NS_ERROR_FAILURE);
+
+ MAI_ATK_OBJECT(atkObj)->FireTextChangeEvent(
+ event->ModifiedText(), event->GetStartOffset(), event->GetLength(),
+ event->IsTextInserted(), event->IsFromUserInput());
+
+ return NS_OK;
+ }
+
+ case nsIAccessibleEvent::EVENT_FOCUS: {
+ a11y::RootAccessible* rootAccWrap = accWrap->RootAccessible();
+ if (rootAccWrap && rootAccWrap->IsActivated()) {
+ atk_focus_tracker_notify(atkObj);
+ // Fire state change event for focus
+ atk_object_notify_state_change(atkObj, ATK_STATE_FOCUSED, true);
+ return NS_OK;
+ }
+ } break;
+
+ case nsIAccessibleEvent::EVENT_NAME_CHANGE: {
+ nsAutoString newName;
+ accessible->Name(newName);
+
+ MaybeFireNameChange(atkObj, newName);
+
+ break;
+ }
+
+ case nsIAccessibleEvent::EVENT_VALUE_CHANGE:
+ case nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE:
+ if (accessible->HasNumericValue()) {
+ // Make sure this is a numeric value. Don't fire for string value
+ // changes (e.g. text editing) ATK values are always numeric.
+ g_object_notify((GObject*)atkObj, "accessible-value");
+ }
+ break;
+
+ case nsIAccessibleEvent::EVENT_SELECTION:
+ case nsIAccessibleEvent::EVENT_SELECTION_ADD:
+ case nsIAccessibleEvent::EVENT_SELECTION_REMOVE: {
+ // XXX: dupe events may be fired
+ AccSelChangeEvent* selChangeEvent = downcast_accEvent(aEvent);
+ g_signal_emit_by_name(
+ AccessibleWrap::GetAtkObject(selChangeEvent->Widget()),
+ "selection_changed");
+ break;
+ }
+
+ case nsIAccessibleEvent::EVENT_SELECTION_WITHIN: {
+ g_signal_emit_by_name(atkObj, "selection_changed");
+ break;
+ }
+
+ case nsIAccessibleEvent::EVENT_ALERT:
+ // A hack using state change showing events as alert events.
+ atk_object_notify_state_change(atkObj, ATK_STATE_SHOWING, true);
+ break;
+
+ case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED:
+ g_signal_emit_by_name(atkObj, "text_selection_changed");
+ break;
+
+ case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED: {
+ AccCaretMoveEvent* caretMoveEvent = downcast_accEvent(aEvent);
+ NS_ASSERTION(caretMoveEvent, "Event needs event data");
+ if (!caretMoveEvent) break;
+
+ int32_t caretOffset = caretMoveEvent->GetCaretOffset();
+ g_signal_emit_by_name(atkObj, "text_caret_moved", caretOffset);
+ } break;
+
+ case nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED:
+ g_signal_emit_by_name(atkObj, "text-attributes-changed");
+ break;
+
+ case nsIAccessibleEvent::EVENT_TABLE_MODEL_CHANGED:
+ g_signal_emit_by_name(atkObj, "model_changed");
+ break;
+
+ case nsIAccessibleEvent::EVENT_TABLE_ROW_INSERT: {
+ AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent);
+ NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE);
+
+ int32_t rowIndex = tableEvent->GetIndex();
+ int32_t numRows = tableEvent->GetCount();
+
+ g_signal_emit_by_name(atkObj, "row_inserted", rowIndex, numRows);
+ } break;
+
+ case nsIAccessibleEvent::EVENT_TABLE_ROW_DELETE: {
+ AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent);
+ NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE);
+
+ int32_t rowIndex = tableEvent->GetIndex();
+ int32_t numRows = tableEvent->GetCount();
+
+ g_signal_emit_by_name(atkObj, "row_deleted", rowIndex, numRows);
+ } break;
+
+ case nsIAccessibleEvent::EVENT_TABLE_ROW_REORDER: {
+ g_signal_emit_by_name(atkObj, "row_reordered");
+ break;
+ }
+
+ case nsIAccessibleEvent::EVENT_TABLE_COLUMN_INSERT: {
+ AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent);
+ NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE);
+
+ int32_t colIndex = tableEvent->GetIndex();
+ int32_t numCols = tableEvent->GetCount();
+ g_signal_emit_by_name(atkObj, "column_inserted", colIndex, numCols);
+ } break;
+
+ case nsIAccessibleEvent::EVENT_TABLE_COLUMN_DELETE: {
+ AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent);
+ NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE);
+
+ int32_t colIndex = tableEvent->GetIndex();
+ int32_t numCols = tableEvent->GetCount();
+ g_signal_emit_by_name(atkObj, "column_deleted", colIndex, numCols);
+ } break;
+
+ case nsIAccessibleEvent::EVENT_TABLE_COLUMN_REORDER:
+ g_signal_emit_by_name(atkObj, "column_reordered");
+ break;
+
+ case nsIAccessibleEvent::EVENT_SECTION_CHANGED:
+ g_signal_emit_by_name(atkObj, "visible_data_changed");
+ break;
+
+ case nsIAccessibleEvent::EVENT_SHOW: {
+ AccMutationEvent* event = downcast_accEvent(aEvent);
+ LocalAccessible* parentAcc =
+ event ? event->LocalParent() : accessible->LocalParent();
+ AtkObject* parent = AccessibleWrap::GetAtkObject(parentAcc);
+ NS_ENSURE_STATE(parent);
+ auto obj = reinterpret_cast<MaiAtkObject*>(atkObj);
+ obj->FireAtkShowHideEvent(parent, true, aEvent->IsFromUserInput());
+ return NS_OK;
+ }
+
+ case nsIAccessibleEvent::EVENT_HIDE: {
+ // XXX - Handle native dialog accessibles.
+ if (!accessible->IsRoot() && accessible->HasARIARole() &&
+ accessible->ARIARole() == roles::DIALOG) {
+ guint id = g_signal_lookup("deactivate", MAI_TYPE_ATK_OBJECT);
+ g_signal_emit(atkObj, id, 0);
+ }
+
+ AccMutationEvent* event = downcast_accEvent(aEvent);
+ LocalAccessible* parentAcc =
+ event ? event->LocalParent() : accessible->LocalParent();
+ AtkObject* parent = AccessibleWrap::GetAtkObject(parentAcc);
+ NS_ENSURE_STATE(parent);
+ auto obj = reinterpret_cast<MaiAtkObject*>(atkObj);
+ obj->FireAtkShowHideEvent(parent, false, aEvent->IsFromUserInput());
+ return NS_OK;
+ }
+
+ /*
+ * Because dealing with menu is very different between nsIAccessible
+ * and ATK, and the menu activity is important, specially transfer the
+ * following two event.
+ * Need more verification by AT test.
+ */
+ case nsIAccessibleEvent::EVENT_MENU_START:
+ case nsIAccessibleEvent::EVENT_MENU_END:
+ break;
+
+ case nsIAccessibleEvent::EVENT_WINDOW_ACTIVATE: {
+ guint id = g_signal_lookup("activate", MAI_TYPE_ATK_OBJECT);
+ g_signal_emit(atkObj, id, 0);
+
+ // Always fire a current focus event after activation.
+ FocusMgr()->ForceFocusEvent();
+ } break;
+
+ case nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE: {
+ guint id = g_signal_lookup("deactivate", MAI_TYPE_ATK_OBJECT);
+ g_signal_emit(atkObj, id, 0);
+ } break;
+
+ case nsIAccessibleEvent::EVENT_WINDOW_MAXIMIZE: {
+ guint id = g_signal_lookup("maximize", MAI_TYPE_ATK_OBJECT);
+ g_signal_emit(atkObj, id, 0);
+ } break;
+
+ case nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE: {
+ guint id = g_signal_lookup("minimize", MAI_TYPE_ATK_OBJECT);
+ g_signal_emit(atkObj, id, 0);
+ } break;
+
+ case nsIAccessibleEvent::EVENT_WINDOW_RESTORE: {
+ guint id = g_signal_lookup("restore", MAI_TYPE_ATK_OBJECT);
+ g_signal_emit(atkObj, id, 0);
+ } break;
+
+ case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE:
+ if (accessible->IsDoc()) {
+ g_signal_emit_by_name(atkObj, "load_complete");
+ }
+ // XXX - Handle native dialog accessibles.
+ if (!accessible->IsRoot() && accessible->HasARIARole() &&
+ accessible->ARIARole() == roles::DIALOG) {
+ guint id = g_signal_lookup("activate", MAI_TYPE_ATK_OBJECT);
+ g_signal_emit(atkObj, id, 0);
+ }
+ break;
+
+ case nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD:
+ if (accessible->IsDoc()) {
+ g_signal_emit_by_name(atkObj, "reload");
+ }
+ break;
+
+ case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED:
+ if (accessible->IsDoc()) {
+ g_signal_emit_by_name(atkObj, "load_stopped");
+ }
+ break;
+
+ case nsIAccessibleEvent::EVENT_MENUPOPUP_START:
+ atk_focus_tracker_notify(atkObj); // fire extra focus event
+ atk_object_notify_state_change(atkObj, ATK_STATE_VISIBLE, true);
+ atk_object_notify_state_change(atkObj, ATK_STATE_SHOWING, true);
+ break;
+
+ case nsIAccessibleEvent::EVENT_MENUPOPUP_END:
+ atk_object_notify_state_change(atkObj, ATK_STATE_VISIBLE, false);
+ atk_object_notify_state_change(atkObj, ATK_STATE_SHOWING, false);
+ break;
+ }
+
+ return NS_OK;
+}
+
+void a11y::ProxyEvent(RemoteAccessible* aTarget, uint32_t aEventType) {
+ AtkObject* wrapper = GetWrapperFor(aTarget);
+
+ switch (aEventType) {
+ case nsIAccessibleEvent::EVENT_FOCUS:
+ atk_focus_tracker_notify(wrapper);
+ atk_object_notify_state_change(wrapper, ATK_STATE_FOCUSED, true);
+ break;
+ case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE:
+ if (aTarget->IsDoc()) {
+ g_signal_emit_by_name(wrapper, "load_complete");
+ }
+ break;
+ case nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD:
+ if (aTarget->IsDoc()) {
+ g_signal_emit_by_name(wrapper, "reload");
+ }
+ break;
+ case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED:
+ if (aTarget->IsDoc()) {
+ g_signal_emit_by_name(wrapper, "load_stopped");
+ }
+ break;
+ case nsIAccessibleEvent::EVENT_MENUPOPUP_START:
+ atk_focus_tracker_notify(wrapper); // fire extra focus event
+ atk_object_notify_state_change(wrapper, ATK_STATE_VISIBLE, true);
+ atk_object_notify_state_change(wrapper, ATK_STATE_SHOWING, true);
+ break;
+ case nsIAccessibleEvent::EVENT_MENUPOPUP_END:
+ atk_object_notify_state_change(wrapper, ATK_STATE_VISIBLE, false);
+ atk_object_notify_state_change(wrapper, ATK_STATE_SHOWING, false);
+ break;
+ case nsIAccessibleEvent::EVENT_ALERT:
+ // A hack using state change showing events as alert events.
+ atk_object_notify_state_change(wrapper, ATK_STATE_SHOWING, true);
+ break;
+ case nsIAccessibleEvent::EVENT_VALUE_CHANGE:
+ g_object_notify((GObject*)wrapper, "accessible-value");
+ break;
+ case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED:
+ g_signal_emit_by_name(wrapper, "text_selection_changed");
+ break;
+ case nsIAccessibleEvent::EVENT_SELECTION_WITHIN:
+ g_signal_emit_by_name(wrapper, "selection_changed");
+ break;
+ case nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED:
+ g_signal_emit_by_name(wrapper, "text-attributes-changed");
+ break;
+ }
+}
+
+void a11y::ProxyStateChangeEvent(RemoteAccessible* aTarget, uint64_t aState,
+ bool aEnabled) {
+ MaiAtkObject* atkObj = MAI_ATK_OBJECT(GetWrapperFor(aTarget));
+ atkObj->FireStateChangeEvent(aState, aEnabled);
+}
+
+void a11y::ProxyCaretMoveEvent(RemoteAccessible* aTarget, int32_t aOffset,
+ bool aIsSelectionCollapsed,
+ int32_t aGranularity) {
+ AtkObject* wrapper = GetWrapperFor(aTarget);
+ g_signal_emit_by_name(wrapper, "text_caret_moved", aOffset);
+}
+
+void MaiAtkObject::FireStateChangeEvent(uint64_t aState, bool aEnabled) {
+ auto state = aState;
+ int32_t stateIndex = -1;
+ while (state > 0) {
+ ++stateIndex;
+ state >>= 1;
+ }
+
+ MOZ_ASSERT(
+ stateIndex >= 0 && stateIndex < static_cast<int32_t>(gAtkStateMapLen),
+ "No ATK state for internal state was found");
+ if (stateIndex < 0 || stateIndex >= static_cast<int32_t>(gAtkStateMapLen)) {
+ return;
+ }
+
+ if (gAtkStateMap[stateIndex].atkState != kNone) {
+ MOZ_ASSERT(gAtkStateMap[stateIndex].stateMapEntryType != kNoStateChange,
+ "State changes should not fired for this state");
+
+ if (gAtkStateMap[stateIndex].stateMapEntryType == kMapOpposite) {
+ aEnabled = !aEnabled;
+ }
+
+ // Fire state change for first state if there is one to map
+ atk_object_notify_state_change(&parent, gAtkStateMap[stateIndex].atkState,
+ aEnabled);
+ }
+}
+
+void a11y::ProxyTextChangeEvent(RemoteAccessible* aTarget,
+ const nsAString& aStr, int32_t aStart,
+ uint32_t aLen, bool aIsInsert, bool aFromUser) {
+ MaiAtkObject* atkObj = MAI_ATK_OBJECT(GetWrapperFor(aTarget));
+ atkObj->FireTextChangeEvent(aStr, aStart, aLen, aIsInsert, aFromUser);
+}
+
+#define OLD_TEXT_INSERTED "text_changed::insert"
+#define OLD_TEXT_REMOVED "text_changed::delete"
+static const char* oldTextChangeStrings[2][2] = {
+ {OLD_TEXT_REMOVED NON_USER_EVENT, OLD_TEXT_INSERTED NON_USER_EVENT},
+ {OLD_TEXT_REMOVED, OLD_TEXT_INSERTED}};
+
+#define TEXT_INSERTED "text-insert"
+#define TEXT_REMOVED "text-remove"
+#define NON_USER_DETAIL "::system"
+static const char* textChangedStrings[2][2] = {
+ {TEXT_REMOVED NON_USER_DETAIL, TEXT_INSERTED NON_USER_DETAIL},
+ {TEXT_REMOVED, TEXT_INSERTED}};
+
+void MaiAtkObject::FireTextChangeEvent(const nsAString& aStr, int32_t aStart,
+ uint32_t aLen, bool aIsInsert,
+ bool aFromUser) {
+ if (gAvailableAtkSignals == eUnknown) {
+ gAvailableAtkSignals = g_signal_lookup("text-insert", G_OBJECT_TYPE(this))
+ ? eHaveNewAtkTextSignals
+ : eNoNewAtkSignals;
+ }
+
+ if (gAvailableAtkSignals == eNoNewAtkSignals) {
+ // XXX remove this code and the gHaveNewTextSignals check when we can
+ // stop supporting old atk since it doesn't really work anyway
+ // see bug 619002
+ const char* signal_name = oldTextChangeStrings[aFromUser][aIsInsert];
+ g_signal_emit_by_name(this, signal_name, aStart, aLen);
+ } else {
+ const char* signal_name = textChangedStrings[aFromUser][aIsInsert];
+ g_signal_emit_by_name(this, signal_name, aStart, aLen,
+ NS_ConvertUTF16toUTF8(aStr).get());
+ }
+}
+
+void a11y::ProxyShowHideEvent(RemoteAccessible* aTarget,
+ RemoteAccessible* aParent, bool aInsert,
+ bool aFromUser) {
+ MaiAtkObject* obj = MAI_ATK_OBJECT(GetWrapperFor(aTarget));
+ obj->FireAtkShowHideEvent(GetWrapperFor(aParent), aInsert, aFromUser);
+}
+
+#define ADD_EVENT "children_changed::add"
+#define HIDE_EVENT "children_changed::remove"
+
+static const char* kMutationStrings[2][2] = {
+ {HIDE_EVENT NON_USER_EVENT, ADD_EVENT NON_USER_EVENT},
+ {HIDE_EVENT, ADD_EVENT},
+};
+
+void MaiAtkObject::FireAtkShowHideEvent(AtkObject* aParent, bool aIsAdded,
+ bool aFromUser) {
+ int32_t indexInParent = getIndexInParentCB(&this->parent);
+ const char* signal_name = kMutationStrings[aFromUser][aIsAdded];
+ g_signal_emit_by_name(aParent, signal_name, indexInParent, this, nullptr);
+}
+
+void a11y::ProxySelectionEvent(RemoteAccessible*, RemoteAccessible* aWidget,
+ uint32_t) {
+ MaiAtkObject* obj = MAI_ATK_OBJECT(GetWrapperFor(aWidget));
+ g_signal_emit_by_name(obj, "selection_changed");
+}
+
+// static
+void AccessibleWrap::GetKeyBinding(Accessible* aAccessible,
+ nsAString& aResult) {
+ // Return all key bindings including access key and keyboard shortcut.
+
+ // Get access key.
+ nsAutoString keyBindingsStr;
+ KeyBinding keyBinding = aAccessible->AccessKey();
+ if (!keyBinding.IsEmpty()) {
+ keyBinding.AppendToString(keyBindingsStr, KeyBinding::eAtkFormat);
+
+ Accessible* parent = aAccessible->Parent();
+ roles::Role role = parent ? parent->Role() : roles::NOTHING;
+ if (role == roles::PARENT_MENUITEM || role == roles::MENUITEM ||
+ role == roles::RADIO_MENU_ITEM || role == roles::CHECK_MENU_ITEM) {
+ // It is submenu, expose keyboard shortcuts from menu hierarchy like
+ // "s;<Alt>f:s"
+ nsAutoString keysInHierarchyStr = keyBindingsStr;
+ do {
+ KeyBinding parentKeyBinding = parent->AccessKey();
+ if (!parentKeyBinding.IsEmpty()) {
+ nsAutoString str;
+ parentKeyBinding.ToString(str, KeyBinding::eAtkFormat);
+ str.Append(':');
+
+ keysInHierarchyStr.Insert(str, 0);
+ }
+ } while ((parent = parent->Parent()) && parent->Role() != roles::MENUBAR);
+
+ keyBindingsStr.Append(';');
+ keyBindingsStr.Append(keysInHierarchyStr);
+ }
+ } else {
+ // No access key, add ';' to point this.
+ keyBindingsStr.Append(';');
+ }
+
+ // Get keyboard shortcut.
+ keyBindingsStr.Append(';');
+ if (LocalAccessible* localAcc = aAccessible->AsLocal()) {
+ keyBinding = localAcc->KeyboardShortcut();
+ if (!keyBinding.IsEmpty()) {
+ keyBinding.AppendToString(keyBindingsStr, KeyBinding::eAtkFormat);
+ }
+ }
+ aResult = keyBindingsStr;
+}
+
+// static
+Accessible* AccessibleWrap::GetColumnHeader(TableAccessible* aAccessible,
+ int32_t aColIdx) {
+ if (!aAccessible) {
+ return nullptr;
+ }
+
+ Accessible* cell = aAccessible->CellAt(0, aColIdx);
+ if (!cell) {
+ return nullptr;
+ }
+
+ // If the cell at the first row is column header then assume it is column
+ // header for all rows,
+ if (cell->Role() == roles::COLUMNHEADER) {
+ return cell;
+ }
+
+ // otherwise get column header for the data cell at the first row.
+ TableCellAccessible* tableCell = cell->AsTableCell();
+ if (!tableCell) {
+ return nullptr;
+ }
+
+ AutoTArray<Accessible*, 10> headerCells;
+ tableCell->ColHeaderCells(&headerCells);
+ if (headerCells.IsEmpty()) {
+ return nullptr;
+ }
+
+ return headerCells[0];
+}
+
+// static
+Accessible* AccessibleWrap::GetRowHeader(TableAccessible* aAccessible,
+ int32_t aRowIdx) {
+ if (!aAccessible) {
+ return nullptr;
+ }
+
+ Accessible* cell = aAccessible->CellAt(aRowIdx, 0);
+ if (!cell) {
+ return nullptr;
+ }
+
+ // If the cell at the first column is row header then assume it is row
+ // header for all columns,
+ if (cell->Role() == roles::ROWHEADER) {
+ return cell;
+ }
+
+ // otherwise get row header for the data cell at the first column.
+ TableCellAccessible* tableCell = cell->AsTableCell();
+ if (!tableCell) {
+ return nullptr;
+ }
+
+ AutoTArray<Accessible*, 10> headerCells;
+ tableCell->RowHeaderCells(&headerCells);
+ if (headerCells.IsEmpty()) {
+ return nullptr;
+ }
+
+ return headerCells[0];
+}
diff --git a/accessible/atk/AccessibleWrap.h b/accessible/atk/AccessibleWrap.h
new file mode 100644
index 0000000000..3ef404534f
--- /dev/null
+++ b/accessible/atk/AccessibleWrap.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __NS_ACCESSIBLE_WRAP_H__
+#define __NS_ACCESSIBLE_WRAP_H__
+
+#include "nsCOMPtr.h"
+#include "LocalAccessible.h"
+
+struct _AtkObject;
+typedef struct _AtkObject AtkObject;
+
+enum AtkProperty {
+ PROP_0, // gobject convention
+ PROP_NAME,
+ PROP_DESCRIPTION,
+ PROP_PARENT, // ancestry has changed
+ PROP_ROLE,
+ PROP_LAYER,
+ PROP_MDI_ZORDER,
+ PROP_TABLE_CAPTION,
+ PROP_TABLE_COLUMN_DESCRIPTION,
+ PROP_TABLE_COLUMN_HEADER,
+ PROP_TABLE_ROW_DESCRIPTION,
+ PROP_TABLE_ROW_HEADER,
+ PROP_TABLE_SUMMARY,
+ PROP_LAST // gobject convention
+};
+
+struct AtkPropertyChange {
+ int32_t type; // property type as listed above
+ void* oldvalue;
+ void* newvalue;
+};
+
+namespace mozilla {
+namespace a11y {
+
+class MaiHyperlink;
+
+/**
+ * Atk specific functionality for an accessibility tree node that originated in
+ * mDoc's content process.
+ *
+ * AccessibleWrap, and its descendents in atk directory provide the
+ * implementation of AtkObject.
+ */
+class AccessibleWrap : public LocalAccessible {
+ public:
+ AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc);
+ virtual ~AccessibleWrap();
+ void ShutdownAtkObject();
+
+ virtual void Shutdown() override;
+
+ // return the atk object for this AccessibleWrap
+ virtual void GetNativeInterface(void** aOutAccessible) override;
+ virtual nsresult HandleAccEvent(AccEvent* aEvent) override;
+
+ AtkObject* GetAtkObject(void);
+ static AtkObject* GetAtkObject(LocalAccessible* aAccessible);
+
+ bool IsValidObject();
+
+ static const char* ReturnString(nsAString& aString) {
+ static nsCString returnedString;
+ CopyUTF16toUTF8(aString, returnedString);
+ return returnedString.get();
+ }
+
+ static void GetKeyBinding(Accessible* aAccessible, nsAString& aResult);
+
+ static Accessible* GetColumnHeader(TableAccessible* aAccessible,
+ int32_t aColIdx);
+ static Accessible* GetRowHeader(TableAccessible* aAccessible,
+ int32_t aRowIdx);
+
+ protected:
+ nsresult FireAtkStateChangeEvent(AccEvent* aEvent, AtkObject* aObject);
+ nsresult FireAtkTextChangedEvent(AccEvent* aEvent, AtkObject* aObject);
+
+ AtkObject* mAtkObject;
+
+ private:
+ uint16_t CreateMaiInterfaces();
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif /* __NS_ACCESSIBLE_WRAP_H__ */
diff --git a/accessible/atk/ApplicationAccessibleWrap.cpp b/accessible/atk/ApplicationAccessibleWrap.cpp
new file mode 100644
index 0000000000..78c7f6047d
--- /dev/null
+++ b/accessible/atk/ApplicationAccessibleWrap.cpp
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ApplicationAccessibleWrap.h"
+
+#include "nsMai.h"
+#include "nsAccessibilityService.h"
+
+#include <gtk/gtk.h>
+#include "atk/atkobject.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+// ApplicationAccessibleWrap
+
+ApplicationAccessibleWrap::ApplicationAccessibleWrap()
+ : ApplicationAccessible() {}
+
+ApplicationAccessibleWrap::~ApplicationAccessibleWrap() {
+ AccessibleWrap::ShutdownAtkObject();
+}
+
+gboolean toplevel_event_watcher(GSignalInvocationHint* ihint,
+ guint n_param_values,
+ const GValue* param_values, gpointer data) {
+ static GQuark sQuark_gecko_acc_obj = 0;
+
+ if (!sQuark_gecko_acc_obj) {
+ sQuark_gecko_acc_obj = g_quark_from_static_string("GeckoAccObj");
+ }
+
+ if (nsAccessibilityService::IsShutdown()) return TRUE;
+
+ GObject* object =
+ reinterpret_cast<GObject*>(g_value_get_object(param_values));
+ if (!GTK_IS_WINDOW(object)) return TRUE;
+
+ AtkObject* child = gtk_widget_get_accessible(GTK_WIDGET(object));
+ AtkRole role = atk_object_get_role(child);
+
+ // GTK native dialog
+ if (!IS_MAI_OBJECT(child) &&
+ (role == ATK_ROLE_DIALOG || role == ATK_ROLE_FILE_CHOOSER ||
+ role == ATK_ROLE_COLOR_CHOOSER || role == ATK_ROLE_FONT_CHOOSER)) {
+ if (data == reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_SHOW)) {
+ // Attach the dialog accessible to app accessible tree
+ LocalAccessible* windowAcc =
+ GetAccService()->AddNativeRootAccessible(child);
+ g_object_set_qdata(G_OBJECT(child), sQuark_gecko_acc_obj,
+ reinterpret_cast<gpointer>(windowAcc));
+
+ } else {
+ // Deattach the dialog accessible
+ LocalAccessible* windowAcc = reinterpret_cast<LocalAccessible*>(
+ g_object_get_qdata(G_OBJECT(child), sQuark_gecko_acc_obj));
+ if (windowAcc) {
+ GetAccService()->RemoveNativeRootAccessible(windowAcc);
+ g_object_set_qdata(G_OBJECT(child), sQuark_gecko_acc_obj, nullptr);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+ENameValueFlag ApplicationAccessibleWrap::Name(nsString& aName) const {
+ // ATK doesn't provide a way to obtain an application name (for example,
+ // Firefox or Thunderbird) like IA2 does. Thus let's return an application
+ // name as accessible name that was used to get a branding name (for example,
+ // Minefield aka nightly Firefox or Daily aka nightly Thunderbird).
+ AppName(aName);
+ return eNameOK;
+}
+
+void ApplicationAccessibleWrap::GetNativeInterface(void** aOutAccessible) {
+ *aOutAccessible = nullptr;
+
+ if (!mAtkObject) {
+ mAtkObject = reinterpret_cast<AtkObject*>(
+ g_object_new(MAI_TYPE_ATK_OBJECT, nullptr));
+ if (!mAtkObject) return;
+
+ atk_object_initialize(mAtkObject, static_cast<Accessible*>(this));
+ mAtkObject->role = ATK_ROLE_INVALID;
+ mAtkObject->layer = ATK_LAYER_INVALID;
+ }
+
+ *aOutAccessible = mAtkObject;
+}
+
+struct AtkRootAccessibleAddedEvent {
+ AtkObject* app_accessible;
+ AtkObject* root_accessible;
+ uint32_t index;
+};
+
+gboolean fireRootAccessibleAddedCB(gpointer data) {
+ AtkRootAccessibleAddedEvent* eventData = (AtkRootAccessibleAddedEvent*)data;
+ g_signal_emit_by_name(eventData->app_accessible, "children_changed::add",
+ eventData->index, eventData->root_accessible, nullptr);
+ g_object_unref(eventData->app_accessible);
+ g_object_unref(eventData->root_accessible);
+ free(data);
+
+ return FALSE;
+}
+
+bool ApplicationAccessibleWrap::InsertChildAt(uint32_t aIdx,
+ LocalAccessible* aChild) {
+ if (!ApplicationAccessible::InsertChildAt(aIdx, aChild)) return false;
+
+ AtkObject* atkAccessible = AccessibleWrap::GetAtkObject(aChild);
+ atk_object_set_parent(atkAccessible, mAtkObject);
+
+ uint32_t count = mChildren.Length();
+
+ // Emit children_changed::add in a timeout
+ // to make sure aRootAccWrap is fully initialized.
+ AtkRootAccessibleAddedEvent* eventData =
+ (AtkRootAccessibleAddedEvent*)malloc(sizeof(AtkRootAccessibleAddedEvent));
+ if (eventData) {
+ eventData->app_accessible = mAtkObject;
+ eventData->root_accessible = atkAccessible;
+ eventData->index = count - 1;
+ g_object_ref(mAtkObject);
+ g_object_ref(atkAccessible);
+ g_timeout_add(0, fireRootAccessibleAddedCB, eventData);
+ }
+
+ return true;
+}
+
+bool ApplicationAccessibleWrap::RemoveChild(LocalAccessible* aChild) {
+ int32_t index = aChild->IndexInParent();
+
+ AtkObject* atkAccessible = AccessibleWrap::GetAtkObject(aChild);
+ atk_object_set_parent(atkAccessible, nullptr);
+ g_signal_emit_by_name(mAtkObject, "children_changed::remove", index,
+ atkAccessible, nullptr);
+
+ return ApplicationAccessible::RemoveChild(aChild);
+}
diff --git a/accessible/atk/ApplicationAccessibleWrap.h b/accessible/atk/ApplicationAccessibleWrap.h
new file mode 100644
index 0000000000..fe14dc045a
--- /dev/null
+++ b/accessible/atk/ApplicationAccessibleWrap.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_a11y_ApplicationAccessibleWrap_h__
+#define mozilla_a11y_ApplicationAccessibleWrap_h__
+
+#include "ApplicationAccessible.h"
+
+namespace mozilla {
+namespace a11y {
+
+class ApplicationAccessibleWrap : public ApplicationAccessible {
+ public:
+ ApplicationAccessibleWrap();
+ virtual ~ApplicationAccessibleWrap();
+
+ // LocalAccessible
+ virtual mozilla::a11y::ENameValueFlag Name(nsString& aName) const override;
+ virtual bool InsertChildAt(uint32_t aIdx, LocalAccessible* aChild) override;
+ virtual bool RemoveChild(LocalAccessible* aChild) override;
+
+ /**
+ * Return the atk object for app root accessible.
+ */
+ virtual void GetNativeInterface(void** aOutAccessible) override;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif /* __NS_APP_ROOT_ACCESSIBLE_H__ */
diff --git a/accessible/atk/DOMtoATK.cpp b/accessible/atk/DOMtoATK.cpp
new file mode 100644
index 0000000000..2c23731bba
--- /dev/null
+++ b/accessible/atk/DOMtoATK.cpp
@@ -0,0 +1,151 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DOMtoATK.h"
+#include "nsUTF8Utils.h"
+
+namespace mozilla {
+namespace a11y {
+
+namespace DOMtoATK {
+
+void AddBOMs(nsACString& aDest, const nsACString& aSource) {
+ uint32_t destlength = 0;
+
+ // First compute how much room we will need.
+ for (uint32_t srci = 0; srci < aSource.Length();) {
+ int bytes = UTF8traits::bytes(aSource[srci]);
+ if (bytes >= 4) {
+ // Non-BMP character, will add a BOM after it.
+ destlength += 3;
+ }
+ // Skip whole character encoding.
+ srci += bytes;
+ destlength += bytes;
+ }
+
+ uint32_t desti = 0; // Index within aDest.
+
+ // Add BOMs after non-BMP characters.
+ aDest.SetLength(destlength);
+ for (uint32_t srci = 0; srci < aSource.Length();) {
+ uint32_t bytes = UTF8traits::bytes(aSource[srci]);
+
+ MOZ_ASSERT(bytes <= aSource.Length() - srci,
+ "We should have the whole sequence");
+
+ // Copy whole sequence.
+ aDest.Replace(desti, bytes, Substring(aSource, srci, bytes));
+ desti += bytes;
+ srci += bytes;
+
+ if (bytes >= 4) {
+ // More than 4 bytes in UTF-8 encoding exactly means more than 16 encoded
+ // bits. This is thus a non-BMP character which needed a surrogate
+ // pair to get encoded in UTF-16, add a BOM after it.
+
+ // And add a BOM after it.
+ aDest.Replace(desti, 3, "\xEF\xBB\xBF");
+ desti += 3;
+ }
+ }
+ MOZ_ASSERT(desti == destlength,
+ "Incoherency between computed length"
+ "and actually translated length");
+}
+
+void ATKStringConverterHelper::AdjustOffsets(gint* aStartOffset,
+ gint* aEndOffset, gint count) {
+ MOZ_ASSERT(!mAdjusted,
+ "DOMtoATK::ATKStringConverterHelper::AdjustOffsets needs to be "
+ "called only once");
+
+ if (*aStartOffset > 0) {
+ (*aStartOffset)--;
+ mStartShifted = true;
+ }
+
+ if (*aEndOffset >= 0 && *aEndOffset < count) {
+ (*aEndOffset)++;
+ mEndShifted = true;
+ }
+
+#ifdef DEBUG
+ mAdjusted = true;
+#endif
+}
+
+gchar* ATKStringConverterHelper::FinishUTF16toUTF8(nsCString& aStr) {
+ int skip = 0;
+
+ if (mStartShifted) {
+ // AdjustOffsets added a leading character.
+
+ MOZ_ASSERT(aStr.Length() > 0, "There should be a leading character");
+ MOZ_ASSERT(
+ static_cast<int>(aStr.Length()) >= UTF8traits::bytes(aStr.CharAt(0)),
+ "The leading character should be complete");
+
+ // drop first character
+ skip = UTF8traits::bytes(aStr.CharAt(0));
+ }
+
+ if (mEndShifted) {
+ // AdjustOffsets added a trailing character.
+
+ MOZ_ASSERT(aStr.Length() > 0, "There should be a trailing character");
+
+ int trail = -1;
+ // Find beginning of last character.
+ for (trail = aStr.Length() - 1; trail >= 0; trail--) {
+ if (!UTF8traits::isInSeq(aStr.CharAt(trail))) {
+ break;
+ }
+ }
+ MOZ_ASSERT(trail >= 0,
+ "There should be at least a whole trailing character");
+ MOZ_ASSERT(trail + UTF8traits::bytes(aStr.CharAt(trail)) ==
+ static_cast<int>(aStr.Length()),
+ "The trailing character should be complete");
+
+ // Drop the last character.
+ aStr.Truncate(trail);
+ }
+
+ // copy and return, libspi will free it
+ return g_strdup(aStr.get() + skip);
+}
+
+gchar* ATKStringConverterHelper::ConvertAdjusted(const nsAString& aStr) {
+ MOZ_ASSERT(mAdjusted,
+ "DOMtoATK::ATKStringConverterHelper::AdjustOffsets needs to be "
+ "called before ATKStringConverterHelper::ConvertAdjusted");
+
+ NS_ConvertUTF16toUTF8 cautoStr(aStr);
+ if (!cautoStr.get()) {
+ return nullptr;
+ }
+
+ nsAutoCString cautoStrBOMs;
+ AddBOMs(cautoStrBOMs, cautoStr);
+ return FinishUTF16toUTF8(cautoStrBOMs);
+}
+
+gchar* Convert(const nsAString& aStr) {
+ NS_ConvertUTF16toUTF8 cautoStr(aStr);
+ if (!cautoStr.get()) {
+ return nullptr;
+ }
+
+ nsAutoCString cautoStrBOMs;
+ AddBOMs(cautoStrBOMs, cautoStr);
+ return g_strdup(cautoStrBOMs.get());
+}
+
+} // namespace DOMtoATK
+
+} // namespace a11y
+} // namespace mozilla
diff --git a/accessible/atk/DOMtoATK.h b/accessible/atk/DOMtoATK.h
new file mode 100644
index 0000000000..322358bc6e
--- /dev/null
+++ b/accessible/atk/DOMtoATK.h
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <glib.h>
+#include <cstdint>
+#include "mozilla/a11y/HyperTextAccessibleBase.h"
+#include "nsCharTraits.h"
+#include "nsString.h"
+
+/**
+ * ATK offsets are counted in unicode codepoints, while DOM offsets are counted
+ * in UTF-16 code units. That makes a difference for non-BMP characters,
+ * which need two UTF-16 code units to be represented (a pair of surrogates),
+ * while they are just one unicode character.
+ *
+ * To keep synchronization between ATK offsets (unicode codepoints) and DOM
+ * offsets (UTF-16 code units), after translation from UTF-16 to UTF-8 we add a
+ * BOM after each non-BMP character (which would otherwise use 2 UTF-16
+ * code units for only 1 unicode codepoint).
+ *
+ * BOMs (Byte Order Marks, U+FEFF, also known as ZERO WIDTH NO-BREAK SPACE, but
+ * that usage is deprecated) normally only appear at the beginning of unicode
+ * files, but their occurrence within text (notably after cut&paste) is not
+ * uncommon, and are thus considered as non-text.
+ *
+ * Since the selection requested through ATK may not contain both surrogates
+ * at the ends of the selection, we need to fetch one UTF-16 code point more
+ * on both side, and get rid of it before returning the string to ATK. The
+ * ATKStringConverterHelper class maintains this, NewATKString should be used
+ * to call it properly.
+ *
+ * In the end,
+ * - if the start is between the high and low surrogates, the UTF-8 result
+ * includes a BOM from it but not the character
+ * - if the end is between the high and low surrogates, the UTF-8 result
+ * includes the character but *not* the BOM
+ * - all non-BMP characters that are fully in the string are in the UTF-8 result
+ * as character followed by BOM
+ */
+namespace mozilla {
+namespace a11y {
+
+namespace DOMtoATK {
+
+/**
+ * Converts a string of accessible text into ATK gchar* string (by adding
+ * BOMs). This can be used when offsets do not need to be adjusted because
+ * ends of the string can not fall between surrogates.
+ */
+gchar* Convert(const nsAString& aStr);
+
+/**
+ * Add a BOM after each non-BMP character.
+ */
+void AddBOMs(nsACString& aDest, const nsACString& aSource);
+
+class ATKStringConverterHelper {
+ public:
+ ATKStringConverterHelper(void)
+ :
+#ifdef DEBUG
+ mAdjusted(false),
+#endif
+ mStartShifted(false),
+ mEndShifted(false) {
+ }
+
+ /**
+ * In order to properly get non-BMP values, offsets need to be changed
+ * to get one character more on each end, so that ConvertUTF16toUTF8 can
+ * convert surrogates even if the originally requested offsets fall between
+ * them.
+ */
+ void AdjustOffsets(gint* aStartOffset, gint* aEndOffset, gint count);
+
+ /**
+ * Converts a string of accessible text with adjusted offsets into ATK
+ * gchar* string (by adding BOMs). Note, AdjustOffsets has to be called
+ * before getting the text passed to this.
+ */
+ gchar* ConvertAdjusted(const nsAString& aStr);
+
+ private:
+ /**
+ * Remove the additional characters requested by PrepareUTF16toUTF8.
+ */
+ gchar* FinishUTF16toUTF8(nsCString& aStr);
+
+#ifdef DEBUG
+ bool mAdjusted;
+#endif
+ bool mStartShifted;
+ bool mEndShifted;
+};
+
+/**
+ * Get text from aAccessible, using ATKStringConverterHelper to properly
+ * introduce appropriate BOMs.
+ */
+inline gchar* NewATKString(HyperTextAccessibleBase* aAccessible,
+ gint aStartOffset, gint aEndOffset) {
+ gint startOffset = aStartOffset, endOffset = aEndOffset;
+ ATKStringConverterHelper converter;
+ converter.AdjustOffsets(&startOffset, &endOffset,
+ gint(aAccessible->CharacterCount()));
+ nsAutoString str;
+ aAccessible->TextSubstring(startOffset, endOffset, str);
+
+ if (str.Length() == 0) {
+ // Bogus offsets, or empty string, either way we do not need conversion.
+ return g_strdup("");
+ }
+
+ return converter.ConvertAdjusted(str);
+}
+
+/**
+ * Get a character from aAccessible, fetching more data as appropriate to
+ * properly get non-BMP characters or a BOM as appropriate.
+ */
+inline gunichar ATKCharacter(HyperTextAccessibleBase* aAccessible,
+ gint aOffset) {
+ // char16_t is unsigned short in Mozilla, gnuichar is guint32 in glib.
+ gunichar character = static_cast<gunichar>(aAccessible->CharAt(aOffset));
+
+ if (NS_IS_LOW_SURROGATE(character)) {
+ // Trailing surrogate, return BOM instead.
+ return 0xFEFF;
+ }
+
+ if (NS_IS_HIGH_SURROGATE(character)) {
+ // Heading surrogate, get the trailing surrogate and combine them.
+ gunichar characterLow =
+ static_cast<gunichar>(aAccessible->CharAt(aOffset + 1));
+
+ if (!NS_IS_LOW_SURROGATE(characterLow)) {
+ // It should have been a trailing surrogate... Flag the error.
+ return 0xFFFD;
+ }
+ return SURROGATE_TO_UCS4(character, characterLow);
+ }
+
+ return character;
+}
+
+} // namespace DOMtoATK
+
+} // namespace a11y
+} // namespace mozilla
diff --git a/accessible/atk/DocAccessibleWrap.cpp b/accessible/atk/DocAccessibleWrap.cpp
new file mode 100644
index 0000000000..f3dfba71ac
--- /dev/null
+++ b/accessible/atk/DocAccessibleWrap.cpp
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DocAccessibleWrap.h"
+#include "mozilla/PresShell.h"
+#include "nsIWidgetListener.h"
+#include "nsTArray.h"
+#include "nsWindow.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+////////////////////////////////////////////////////////////////////////////////
+// DocAccessibleWrap
+////////////////////////////////////////////////////////////////////////////////
+
+DocAccessibleWrap::DocAccessibleWrap(dom::Document* aDocument,
+ PresShell* aPresShell)
+ : DocAccessible(aDocument, aPresShell) {}
+
+DocAccessibleWrap::~DocAccessibleWrap() {}
+
+bool DocAccessibleWrap::IsActivated() {
+ if (nsWindow* window = nsWindow::GetFocusedWindow()) {
+ if (nsIWidgetListener* listener = window->GetWidgetListener()) {
+ if (PresShell* presShell = listener->GetPresShell()) {
+ return presShell == PresShellPtr();
+ }
+ }
+ }
+
+ return false;
+}
diff --git a/accessible/atk/DocAccessibleWrap.h b/accessible/atk/DocAccessibleWrap.h
new file mode 100644
index 0000000000..883a4b8f0a
--- /dev/null
+++ b/accessible/atk/DocAccessibleWrap.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* For documentation of the accessibility architecture,
+ * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
+ */
+
+#ifndef mozilla_a11y_DocAccessibleWrap_h__
+#define mozilla_a11y_DocAccessibleWrap_h__
+
+#include "DocAccessible.h"
+
+namespace mozilla {
+
+class PresShell;
+
+namespace a11y {
+
+class DocAccessibleWrap : public DocAccessible {
+ public:
+ DocAccessibleWrap(dom::Document* aDocument, PresShell* aPresShell);
+ virtual ~DocAccessibleWrap();
+
+ bool IsActivated();
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/atk/HyperTextAccessibleWrap.h b/accessible/atk/HyperTextAccessibleWrap.h
new file mode 100644
index 0000000000..2c5745a2b3
--- /dev/null
+++ b/accessible/atk/HyperTextAccessibleWrap.h
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_a11y_HyperTextAccessibleWrap_h__
+#define mozilla_a11y_HyperTextAccessibleWrap_h__
+
+#include "HyperTextAccessible.h"
+
+namespace mozilla {
+namespace a11y {
+
+typedef class HyperTextAccessible HyperTextAccessibleWrap;
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/atk/InterfaceInitFuncs.h b/accessible/atk/InterfaceInitFuncs.h
new file mode 100644
index 0000000000..43ed8ff4ee
--- /dev/null
+++ b/accessible/atk/InterfaceInitFuncs.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ATK_INTERFACE_INIT_FUNCS_H_
+#define ATK_INTERFACE_INIT_FUNCS_H_
+
+#include <atk/atk.h>
+
+namespace mozilla {
+namespace a11y {
+
+class AccessibleWrap;
+
+} // namespace a11y
+} // namespace mozilla
+
+extern "C" {
+void actionInterfaceInitCB(AtkActionIface* aIface);
+void componentInterfaceInitCB(AtkComponentIface* aIface);
+void documentInterfaceInitCB(AtkDocumentIface* aIface);
+void editableTextInterfaceInitCB(AtkEditableTextIface* aIface);
+void hyperlinkImplInterfaceInitCB(AtkHyperlinkImplIface* aIface);
+void hypertextInterfaceInitCB(AtkHypertextIface* aIface);
+void imageInterfaceInitCB(AtkImageIface* aIface);
+void selectionInterfaceInitCB(AtkSelectionIface* aIface);
+void tableInterfaceInitCB(AtkTableIface* aIface);
+void tableCellInterfaceInitCB(AtkTableCellIface* aIface);
+void textInterfaceInitCB(AtkTextIface* aIface);
+void valueInterfaceInitCB(AtkValueIface* aIface);
+}
+
+/**
+ * XXX these should live in a file of utils for atk.
+ */
+AtkObject* refAccessibleAtPointHelper(AtkObject* aAtkObj, gint aX, gint aY,
+ AtkCoordType aCoordType);
+void getExtentsHelper(AtkObject* aAtkObj, gint* aX, gint* aY, gint* aWidth,
+ gint* aHeight, AtkCoordType aCoordType);
+
+#endif // ATK_INTERFACE_INIT_FUNCS_H_
diff --git a/accessible/atk/Platform.cpp b/accessible/atk/Platform.cpp
new file mode 100644
index 0000000000..e166fcfc32
--- /dev/null
+++ b/accessible/atk/Platform.cpp
@@ -0,0 +1,271 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Platform.h"
+
+#include "nsIAccessibleEvent.h"
+#include "nsIGSettingsService.h"
+#include "nsMai.h"
+#include "nsServiceManagerUtils.h"
+#include "prenv.h"
+#include "prlink.h"
+
+#ifdef MOZ_ENABLE_DBUS
+# include <dbus/dbus.h>
+#endif
+#include <gtk/gtk.h>
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+int atkMajorVersion = 1, atkMinorVersion = 12, atkMicroVersion = 0;
+
+GType (*gAtkTableCellGetTypeFunc)();
+
+extern "C" {
+typedef GType (*AtkGetTypeType)(void);
+typedef void (*AtkBridgeAdaptorInit)(int*, char**[]);
+}
+
+static PRLibrary* sATKLib = nullptr;
+static const char sATKLibName[] = "libatk-1.0.so.0";
+static const char sATKHyperlinkImplGetTypeSymbol[] =
+ "atk_hyperlink_impl_get_type";
+
+gboolean toplevel_event_watcher(GSignalInvocationHint*, guint, const GValue*,
+ gpointer);
+static bool sToplevel_event_hook_added = false;
+static gulong sToplevel_show_hook = 0;
+static gulong sToplevel_hide_hook = 0;
+
+GType g_atk_hyperlink_impl_type = G_TYPE_INVALID;
+
+struct AtkBridgeModule {
+ const char* libName;
+ PRLibrary* lib;
+ const char* initName;
+ AtkBridgeAdaptorInit init;
+};
+
+static AtkBridgeModule sAtkBridge = {"libatk-bridge-2.0.so.0", nullptr,
+ "atk_bridge_adaptor_init", nullptr};
+
+static nsresult LoadGtkModule(AtkBridgeModule& aModule) {
+ NS_ENSURE_ARG(aModule.libName);
+
+ if (!(aModule.lib = PR_LoadLibrary(aModule.libName))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // we have loaded the library, try to get the function ptrs
+ if (!(aModule.init = (AtkBridgeAdaptorInit)PR_FindFunctionSymbol(
+ aModule.lib, aModule.initName))) {
+ // fail, :(
+ PR_UnloadLibrary(aModule.lib);
+ aModule.lib = nullptr;
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+void a11y::PlatformInit() {
+ if (!ShouldA11yBeEnabled()) return;
+
+ sATKLib = PR_LoadLibrary(sATKLibName);
+ if (!sATKLib) return;
+
+ AtkGetTypeType pfn_atk_hyperlink_impl_get_type =
+ (AtkGetTypeType)PR_FindFunctionSymbol(sATKLib,
+ sATKHyperlinkImplGetTypeSymbol);
+ if (pfn_atk_hyperlink_impl_get_type) {
+ g_atk_hyperlink_impl_type = pfn_atk_hyperlink_impl_get_type();
+ }
+
+ gAtkTableCellGetTypeFunc =
+ (GType(*)())PR_FindFunctionSymbol(sATKLib, "atk_table_cell_get_type");
+
+ const char* (*atkGetVersion)() =
+ (const char* (*)())PR_FindFunctionSymbol(sATKLib, "atk_get_version");
+ if (atkGetVersion) {
+ const char* version = atkGetVersion();
+ if (version) {
+ char* endPtr = nullptr;
+ atkMajorVersion = strtol(version, &endPtr, 10);
+ if (atkMajorVersion != 0L) {
+ atkMinorVersion = strtol(endPtr + 1, &endPtr, 10);
+ if (atkMinorVersion != 0L) {
+ atkMicroVersion = strtol(endPtr + 1, &endPtr, 10);
+ }
+ }
+ }
+ }
+
+ // Initialize the MAI Utility class, it will overwrite gail_util.
+ g_type_class_unref(g_type_class_ref(mai_util_get_type()));
+
+ // Init atk-bridge now
+ PR_SetEnv("NO_AT_BRIDGE=0");
+ nsresult rv = LoadGtkModule(sAtkBridge);
+ if (NS_SUCCEEDED(rv)) {
+ (*sAtkBridge.init)(nullptr, nullptr);
+ }
+
+ if (!sToplevel_event_hook_added) {
+ sToplevel_event_hook_added = true;
+ sToplevel_show_hook = g_signal_add_emission_hook(
+ g_signal_lookup("show", GTK_TYPE_WINDOW), 0, toplevel_event_watcher,
+ reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_SHOW), nullptr);
+ sToplevel_hide_hook = g_signal_add_emission_hook(
+ g_signal_lookup("hide", GTK_TYPE_WINDOW), 0, toplevel_event_watcher,
+ reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_HIDE), nullptr);
+ }
+}
+
+void a11y::PlatformShutdown() {
+ if (sToplevel_event_hook_added) {
+ sToplevel_event_hook_added = false;
+ g_signal_remove_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW),
+ sToplevel_show_hook);
+ g_signal_remove_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW),
+ sToplevel_hide_hook);
+ }
+
+ if (sAtkBridge.lib) {
+ // Do not shutdown/unload atk-bridge,
+ // an exit function registered will take care of it
+ // PR_UnloadLibrary(sAtkBridge.lib);
+ sAtkBridge.lib = nullptr;
+ sAtkBridge.init = nullptr;
+ }
+ // if (sATKLib) {
+ // PR_UnloadLibrary(sATKLib);
+ // sATKLib = nullptr;
+ // }
+}
+
+static const char sAccEnv[] = "GNOME_ACCESSIBILITY";
+#ifdef MOZ_ENABLE_DBUS
+static DBusPendingCall* sPendingCall = nullptr;
+#endif
+
+void a11y::PreInit() {
+#ifdef MOZ_ENABLE_DBUS
+ static bool sChecked = FALSE;
+ if (sChecked) return;
+
+ sChecked = TRUE;
+
+ // dbus is only checked if GNOME_ACCESSIBILITY is unset
+ // also make sure that a session bus address is available to prevent dbus from
+ // starting a new one. Dbus confuses the test harness when it creates a new
+ // process (see bug 693343)
+ if (PR_GetEnv(sAccEnv) || !PR_GetEnv("DBUS_SESSION_BUS_ADDRESS")) return;
+
+ DBusConnection* bus = dbus_bus_get(DBUS_BUS_SESSION, nullptr);
+ if (!bus) return;
+
+ dbus_connection_set_exit_on_disconnect(bus, FALSE);
+
+ static const char* iface = "org.a11y.Status";
+ static const char* member = "IsEnabled";
+ DBusMessage* message;
+ message =
+ dbus_message_new_method_call("org.a11y.Bus", "/org/a11y/bus",
+ "org.freedesktop.DBus.Properties", "Get");
+ if (!message) goto dbus_done;
+
+ dbus_message_append_args(message, DBUS_TYPE_STRING, &iface, DBUS_TYPE_STRING,
+ &member, DBUS_TYPE_INVALID);
+ dbus_connection_send_with_reply(bus, message, &sPendingCall, 1000);
+ dbus_message_unref(message);
+
+dbus_done:
+ dbus_connection_unref(bus);
+#endif
+}
+
+bool a11y::ShouldA11yBeEnabled() {
+ static bool sChecked = false, sShouldEnable = false;
+ if (sChecked) return sShouldEnable;
+
+ sChecked = true;
+
+ EPlatformDisabledState disabledState = PlatformDisabledState();
+ if (disabledState == ePlatformIsDisabled) {
+ return sShouldEnable = false;
+ }
+ if (disabledState == ePlatformIsForceEnabled) {
+ return sShouldEnable = true;
+ }
+
+ // check if accessibility enabled/disabled by environment variable
+ const char* envValue = PR_GetEnv(sAccEnv);
+ if (envValue) return sShouldEnable = !!atoi(envValue);
+
+#ifdef MOZ_ENABLE_DBUS
+ PreInit();
+ bool dbusSuccess = false;
+ DBusMessage* reply = nullptr;
+ if (!sPendingCall) goto dbus_done;
+
+ dbus_pending_call_block(sPendingCall);
+ reply = dbus_pending_call_steal_reply(sPendingCall);
+ dbus_pending_call_unref(sPendingCall);
+ sPendingCall = nullptr;
+ if (!reply ||
+ dbus_message_get_type(reply) != DBUS_MESSAGE_TYPE_METHOD_RETURN ||
+ strcmp(dbus_message_get_signature(reply), DBUS_TYPE_VARIANT_AS_STRING)) {
+ goto dbus_done;
+ }
+
+ DBusMessageIter iter, iter_variant, iter_struct;
+ dbus_bool_t dResult;
+ dbus_message_iter_init(reply, &iter);
+ dbus_message_iter_recurse(&iter, &iter_variant);
+ switch (dbus_message_iter_get_arg_type(&iter_variant)) {
+ case DBUS_TYPE_STRUCT:
+ // at-spi2-core 2.2.0-2.2.1 had a bug where it returned a struct
+ dbus_message_iter_recurse(&iter_variant, &iter_struct);
+ if (dbus_message_iter_get_arg_type(&iter_struct) == DBUS_TYPE_BOOLEAN) {
+ dbus_message_iter_get_basic(&iter_struct, &dResult);
+ sShouldEnable = dResult;
+ dbusSuccess = true;
+ }
+
+ break;
+ case DBUS_TYPE_BOOLEAN:
+ dbus_message_iter_get_basic(&iter_variant, &dResult);
+ sShouldEnable = dResult;
+ dbusSuccess = true;
+ break;
+ default:
+ break;
+ }
+
+dbus_done:
+ if (reply) dbus_message_unref(reply);
+
+ if (dbusSuccess) return sShouldEnable;
+#endif
+
+// check GSettings
+#define GSETINGS_A11Y_INTERFACE "org.gnome.desktop.interface"
+#define GSETINGS_A11Y_KEY "toolkit-accessibility"
+ nsCOMPtr<nsIGSettingsService> gsettings =
+ do_GetService(NS_GSETTINGSSERVICE_CONTRACTID);
+ nsCOMPtr<nsIGSettingsCollection> a11y_settings;
+
+ if (gsettings) {
+ gsettings->GetCollectionForSchema(nsLiteralCString(GSETINGS_A11Y_INTERFACE),
+ getter_AddRefs(a11y_settings));
+ if (a11y_settings) {
+ a11y_settings->GetBoolean(nsLiteralCString(GSETINGS_A11Y_KEY),
+ &sShouldEnable);
+ }
+ }
+
+ return sShouldEnable;
+}
diff --git a/accessible/atk/RootAccessibleWrap.cpp b/accessible/atk/RootAccessibleWrap.cpp
new file mode 100644
index 0000000000..7d5534c564
--- /dev/null
+++ b/accessible/atk/RootAccessibleWrap.cpp
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RootAccessibleWrap.h"
+
+#include "atk/atkobject.h"
+#include "nsTArray.h"
+
+#include <glib-object.h>
+
+using namespace mozilla::a11y;
+
+GtkWindowAccessible::GtkWindowAccessible(AtkObject* aAccessible)
+ : DummyAccessible() {
+ g_object_ref(aAccessible);
+ mAtkObject = aAccessible;
+}
+
+GtkWindowAccessible::~GtkWindowAccessible() {
+ g_object_unref(mAtkObject);
+ mAtkObject = nullptr;
+}
diff --git a/accessible/atk/RootAccessibleWrap.h b/accessible/atk/RootAccessibleWrap.h
new file mode 100644
index 0000000000..75038f698d
--- /dev/null
+++ b/accessible/atk/RootAccessibleWrap.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_a11y_RootAccessibleWrap_h__
+#define mozilla_a11y_RootAccessibleWrap_h__
+
+#include "BaseAccessibles.h"
+#include "RootAccessible.h"
+
+namespace mozilla {
+namespace a11y {
+
+typedef RootAccessible RootAccessibleWrap;
+
+/* GtkWindowAccessible is the accessible class for gtk+ native window.
+ * The instance of GtkWindowAccessible is a child of MaiAppRoot instance.
+ * It is added into root when the toplevel window is created, and removed
+ * from root when the toplevel window is destroyed.
+ */
+class GtkWindowAccessible final : public DummyAccessible {
+ public:
+ explicit GtkWindowAccessible(AtkObject* aAccessible);
+ virtual ~GtkWindowAccessible();
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif /* mozilla_a11y_Root_Accessible_Wrap_h__ */
diff --git a/accessible/atk/UtilInterface.cpp b/accessible/atk/UtilInterface.cpp
new file mode 100644
index 0000000000..8389e09f80
--- /dev/null
+++ b/accessible/atk/UtilInterface.cpp
@@ -0,0 +1,347 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ApplicationAccessible.h"
+#include "mozilla/Likely.h"
+#include "nsAccessibilityService.h"
+#include "nsMai.h"
+
+#include <atk/atkobject.h>
+#include <atk/atkutil.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+typedef AtkUtil MaiUtil;
+typedef AtkUtilClass MaiUtilClass;
+
+#define MAI_VERSION MOZILLA_VERSION
+#define MAI_NAME "Gecko"
+
+extern "C" {
+static guint (*gail_add_global_event_listener)(GSignalEmissionHook listener,
+ const gchar* event_type);
+static void (*gail_remove_global_event_listener)(guint remove_listener);
+static void (*gail_remove_key_event_listener)(guint remove_listener);
+static AtkObject* (*gail_get_root)();
+}
+
+struct MaiUtilListenerInfo {
+ gint key;
+ guint signal_id;
+ gulong hook_id;
+ // For window create/destory/minimize/maximize/restore/activate/deactivate
+ // events, we'll chain gail_util's add/remove_global_event_listener.
+ // So we store the listenerid returned by gail's add_global_event_listener
+ // in this structure to call gail's remove_global_event_listener later.
+ guint gail_listenerid;
+};
+
+static GHashTable* sListener_list = nullptr;
+static gint sListener_idx = 1;
+
+extern "C" {
+static guint add_listener(GSignalEmissionHook listener,
+ const gchar* object_type, const gchar* signal,
+ const gchar* hook_data, guint gail_listenerid = 0) {
+ GType type;
+ guint signal_id;
+ gint rc = 0;
+
+ type = g_type_from_name(object_type);
+ if (type) {
+ signal_id = g_signal_lookup(signal, type);
+ if (signal_id > 0) {
+ MaiUtilListenerInfo* listener_info;
+
+ rc = sListener_idx;
+
+ listener_info =
+ (MaiUtilListenerInfo*)g_malloc(sizeof(MaiUtilListenerInfo));
+ listener_info->key = sListener_idx;
+ listener_info->hook_id = g_signal_add_emission_hook(
+ signal_id, 0, listener, g_strdup(hook_data), (GDestroyNotify)g_free);
+ listener_info->signal_id = signal_id;
+ listener_info->gail_listenerid = gail_listenerid;
+
+ g_hash_table_insert(sListener_list, &(listener_info->key), listener_info);
+ sListener_idx++;
+ } else {
+ g_warning("Invalid signal type %s\n", signal);
+ }
+ } else {
+ g_warning("Invalid object type %s\n", object_type);
+ }
+ return rc;
+}
+
+static guint mai_util_add_global_event_listener(GSignalEmissionHook listener,
+ const gchar* event_type) {
+ guint rc = 0;
+ gchar** split_string;
+
+ split_string = g_strsplit(event_type, ":", 3);
+
+ if (split_string) {
+ if (!strcmp("window", split_string[0])) {
+ guint gail_listenerid = 0;
+ if (gail_add_global_event_listener) {
+ // call gail's function to track gtk native window events
+ gail_listenerid = gail_add_global_event_listener(listener, event_type);
+ }
+
+ rc = add_listener(listener, "MaiAtkObject", split_string[1], event_type,
+ gail_listenerid);
+ } else {
+ rc = add_listener(listener, split_string[1], split_string[2], event_type);
+ }
+ g_strfreev(split_string);
+ }
+ return rc;
+}
+
+static void mai_util_remove_global_event_listener(guint remove_listener) {
+ if (remove_listener > 0) {
+ MaiUtilListenerInfo* listener_info;
+ gint tmp_idx = remove_listener;
+
+ listener_info =
+ (MaiUtilListenerInfo*)g_hash_table_lookup(sListener_list, &tmp_idx);
+
+ if (listener_info != nullptr) {
+ if (gail_remove_global_event_listener && listener_info->gail_listenerid) {
+ gail_remove_global_event_listener(listener_info->gail_listenerid);
+ }
+
+ /* Hook id of 0 and signal id of 0 are invalid */
+ if (listener_info->hook_id != 0 && listener_info->signal_id != 0) {
+ /* Remove the emission hook */
+ g_signal_remove_emission_hook(listener_info->signal_id,
+ listener_info->hook_id);
+
+ /* Remove the element from the hash */
+ g_hash_table_remove(sListener_list, &tmp_idx);
+ } else {
+ g_warning("Invalid listener hook_id %ld or signal_id %d\n",
+ listener_info->hook_id, listener_info->signal_id);
+ }
+ } else {
+ // atk-bridge is initialized with gail (e.g. yelp)
+ // try gail_remove_global_event_listener
+ if (gail_remove_global_event_listener) {
+ return gail_remove_global_event_listener(remove_listener);
+ }
+
+ g_warning("No listener with the specified listener id %d",
+ remove_listener);
+ }
+ } else {
+ g_warning("Invalid listener_id %d", remove_listener);
+ }
+}
+
+static AtkKeyEventStruct* atk_key_event_from_gdk_event_key(GdkEventKey* key) {
+ AtkKeyEventStruct* event = g_new0(AtkKeyEventStruct, 1);
+ switch (key->type) {
+ case GDK_KEY_PRESS:
+ event->type = ATK_KEY_EVENT_PRESS;
+ break;
+ case GDK_KEY_RELEASE:
+ event->type = ATK_KEY_EVENT_RELEASE;
+ break;
+ default:
+ g_assert_not_reached();
+ return nullptr;
+ }
+ event->state = key->state;
+ event->keyval = key->keyval;
+ event->length = key->length;
+ if (key->string && key->string[0] &&
+ g_unichar_isgraph(g_utf8_get_char(key->string))) {
+ event->string = key->string;
+ } else if (key->type == GDK_KEY_PRESS || key->type == GDK_KEY_RELEASE) {
+ event->string = gdk_keyval_name(key->keyval);
+ }
+ event->keycode = key->hardware_keycode;
+ event->timestamp = key->time;
+
+ return event;
+}
+
+struct MaiKeyEventInfo {
+ AtkKeyEventStruct* key_event;
+ gpointer func_data;
+};
+
+union AtkKeySnoopFuncPointer {
+ AtkKeySnoopFunc func_ptr;
+ gpointer data;
+};
+
+static gboolean notify_hf(gpointer key, gpointer value, gpointer data) {
+ MaiKeyEventInfo* info = (MaiKeyEventInfo*)data;
+ AtkKeySnoopFuncPointer atkKeySnoop;
+ atkKeySnoop.data = value;
+ return (atkKeySnoop.func_ptr)(info->key_event, info->func_data) ? TRUE
+ : FALSE;
+}
+
+static void insert_hf(gpointer key, gpointer value, gpointer data) {
+ GHashTable* new_table = (GHashTable*)data;
+ g_hash_table_insert(new_table, key, value);
+}
+
+static GHashTable* sKey_listener_list = nullptr;
+
+static gint mai_key_snooper(GtkWidget* the_widget, GdkEventKey* event,
+ gpointer func_data) {
+ /* notify each AtkKeySnoopFunc in turn... */
+
+ MaiKeyEventInfo* info = g_new0(MaiKeyEventInfo, 1);
+ gint consumed = 0;
+ if (sKey_listener_list) {
+ GHashTable* new_hash = g_hash_table_new(nullptr, nullptr);
+ g_hash_table_foreach(sKey_listener_list, insert_hf, new_hash);
+ info->key_event = atk_key_event_from_gdk_event_key(event);
+ info->func_data = func_data;
+ consumed = g_hash_table_foreach_steal(new_hash, notify_hf, info);
+ g_hash_table_destroy(new_hash);
+ g_free(info->key_event);
+ }
+ g_free(info);
+ return (consumed ? 1 : 0);
+}
+
+static guint sKey_snooper_id = 0;
+
+static guint mai_util_add_key_event_listener(AtkKeySnoopFunc listener,
+ gpointer data) {
+ if (MOZ_UNLIKELY(!listener)) {
+ return 0;
+ }
+
+ static guint key = 0;
+
+ if (!sKey_listener_list) {
+ sKey_listener_list = g_hash_table_new(nullptr, nullptr);
+ }
+
+ // If we have no registered event listeners then we need to (re)install the
+ // key event snooper.
+ if (g_hash_table_size(sKey_listener_list) == 0) {
+ sKey_snooper_id = gtk_key_snooper_install(mai_key_snooper, data);
+ }
+
+ AtkKeySnoopFuncPointer atkKeySnoop;
+ atkKeySnoop.func_ptr = listener;
+ key++;
+ g_hash_table_insert(sKey_listener_list, GUINT_TO_POINTER(key),
+ atkKeySnoop.data);
+ return key;
+}
+
+static void mai_util_remove_key_event_listener(guint remove_listener) {
+ if (!sKey_listener_list) {
+ // atk-bridge is initialized with gail (e.g. yelp)
+ // try gail_remove_key_event_listener
+ return gail_remove_key_event_listener(remove_listener);
+ }
+
+ g_hash_table_remove(sKey_listener_list, GUINT_TO_POINTER(remove_listener));
+ if (g_hash_table_size(sKey_listener_list) == 0) {
+ gtk_key_snooper_remove(sKey_snooper_id);
+ }
+}
+
+static AtkObject* mai_util_get_root() {
+ ApplicationAccessible* app = ApplicationAcc();
+ if (app) return app->GetAtkObject();
+
+ // We've shutdown, try to use gail instead
+ // (to avoid assert in spi_atk_tidy_windows())
+ // XXX tbsaunde then why didn't we replace the gail atk_util impl?
+ if (gail_get_root) return gail_get_root();
+
+ return nullptr;
+}
+
+static const gchar* mai_util_get_toolkit_name() { return MAI_NAME; }
+
+static const gchar* mai_util_get_toolkit_version() { return MAI_VERSION; }
+
+static void _listener_info_destroy(gpointer data) { g_free(data); }
+
+static void window_added(AtkObject* atk_obj, guint index, AtkObject* child) {
+ if (!IS_MAI_OBJECT(child)) return;
+
+ static guint id = g_signal_lookup("create", MAI_TYPE_ATK_OBJECT);
+ g_signal_emit(child, id, 0);
+}
+
+static void window_removed(AtkObject* atk_obj, guint index, AtkObject* child) {
+ if (!IS_MAI_OBJECT(child)) return;
+
+ static guint id = g_signal_lookup("destroy", MAI_TYPE_ATK_OBJECT);
+ g_signal_emit(child, id, 0);
+}
+
+static void UtilInterfaceInit(MaiUtilClass* klass) {
+ AtkUtilClass* atk_class;
+ gpointer data;
+
+ data = g_type_class_peek(ATK_TYPE_UTIL);
+ atk_class = ATK_UTIL_CLASS(data);
+
+ // save gail function pointer
+ gail_add_global_event_listener = atk_class->add_global_event_listener;
+ gail_remove_global_event_listener = atk_class->remove_global_event_listener;
+ gail_remove_key_event_listener = atk_class->remove_key_event_listener;
+ gail_get_root = atk_class->get_root;
+
+ atk_class->add_global_event_listener = mai_util_add_global_event_listener;
+ atk_class->remove_global_event_listener =
+ mai_util_remove_global_event_listener;
+ atk_class->add_key_event_listener = mai_util_add_key_event_listener;
+ atk_class->remove_key_event_listener = mai_util_remove_key_event_listener;
+ atk_class->get_root = mai_util_get_root;
+ atk_class->get_toolkit_name = mai_util_get_toolkit_name;
+ atk_class->get_toolkit_version = mai_util_get_toolkit_version;
+
+ sListener_list = g_hash_table_new_full(g_int_hash, g_int_equal, nullptr,
+ _listener_info_destroy);
+ // Keep track of added/removed windows.
+ AtkObject* root = atk_get_root();
+ g_signal_connect(root, "children-changed::add", (GCallback)window_added,
+ nullptr);
+ g_signal_connect(root, "children-changed::remove", (GCallback)window_removed,
+ nullptr);
+}
+}
+
+GType mai_util_get_type() {
+ static GType type = 0;
+
+ if (!type) {
+ static const GTypeInfo tinfo = {
+ sizeof(MaiUtilClass),
+ (GBaseInitFunc) nullptr, /* base init */
+ (GBaseFinalizeFunc) nullptr, /* base finalize */
+ (GClassInitFunc)UtilInterfaceInit, /* class init */
+ (GClassFinalizeFunc) nullptr, /* class finalize */
+ nullptr, /* class data */
+ sizeof(MaiUtil), /* instance size */
+ 0, /* nb preallocs */
+ (GInstanceInitFunc) nullptr, /* instance init */
+ nullptr /* value table */
+ };
+
+ type =
+ g_type_register_static(ATK_TYPE_UTIL, "MaiUtil", &tinfo, GTypeFlags(0));
+ }
+ return type;
+}
diff --git a/accessible/atk/moz.build b/accessible/atk/moz.build
new file mode 100644
index 0000000000..57b86622b6
--- /dev/null
+++ b/accessible/atk/moz.build
@@ -0,0 +1,66 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS.mozilla.a11y += [
+ "AccessibleWrap.h",
+ "HyperTextAccessibleWrap.h",
+]
+
+SOURCES += [
+ "AccessibleWrap.cpp",
+ "ApplicationAccessibleWrap.cpp",
+ "DocAccessibleWrap.cpp",
+ "DOMtoATK.cpp",
+ "nsMaiHyperlink.cpp",
+ "nsMaiInterfaceAction.cpp",
+ "nsMaiInterfaceComponent.cpp",
+ "nsMaiInterfaceDocument.cpp",
+ "nsMaiInterfaceEditableText.cpp",
+ "nsMaiInterfaceHyperlinkImpl.cpp",
+ "nsMaiInterfaceHypertext.cpp",
+ "nsMaiInterfaceImage.cpp",
+ "nsMaiInterfaceSelection.cpp",
+ "nsMaiInterfaceTable.cpp",
+ "nsMaiInterfaceTableCell.cpp",
+ "nsMaiInterfaceText.cpp",
+ "nsMaiInterfaceValue.cpp",
+ "Platform.cpp",
+ "RootAccessibleWrap.cpp",
+ "UtilInterface.cpp",
+]
+
+LOCAL_INCLUDES += [
+ "/accessible/base",
+ "/accessible/generic",
+ "/accessible/html",
+ "/accessible/ipc",
+ "/accessible/ipc/other",
+ "/accessible/xpcom",
+ "/accessible/xul",
+ "/layout/generic",
+ "/other-licenses/atk-1.0",
+ "/widget",
+ "/widget/gtk",
+]
+
+FINAL_LIBRARY = "xul"
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
+ CFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
+ CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
+
+if CONFIG["MOZ_ENABLE_DBUS"]:
+ CXXFLAGS += CONFIG["MOZ_DBUS_CFLAGS"]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+if CONFIG["CC_TYPE"] in ("clang", "gcc"):
+ # Used in G_DEFINE_TYPE_EXTENDED macro, probably fixed in newer glib /
+ # gobject headers. See bug 1243331 comment 3.
+ CXXFLAGS += [
+ "-Wno-error=unused-function",
+ "-Wno-unused-local-typedefs",
+ ]
diff --git a/accessible/atk/nsMai.h b/accessible/atk/nsMai.h
new file mode 100644
index 0000000000..175a3c64ff
--- /dev/null
+++ b/accessible/atk/nsMai.h
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __NS_MAI_H__
+#define __NS_MAI_H__
+
+#include <atk/atk.h>
+#include <glib.h>
+#include <glib-object.h>
+
+#include "AccessibleWrap.h"
+
+namespace mozilla {
+namespace a11y {
+class RemoteAccessible;
+class Accessible;
+} // namespace a11y
+} // namespace mozilla
+
+#define MAI_TYPE_ATK_OBJECT (mai_atk_object_get_type())
+#define MAI_ATK_OBJECT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), MAI_TYPE_ATK_OBJECT, MaiAtkObject))
+#define MAI_ATK_OBJECT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), MAI_TYPE_ATK_OBJECT, MaiAtkObjectClass))
+#define IS_MAI_OBJECT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), MAI_TYPE_ATK_OBJECT))
+#define IS_MAI_OBJECT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), MAI_TYPE_ATK_OBJECT))
+#define MAI_ATK_OBJECT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), MAI_TYPE_ATK_OBJECT, MaiAtkObjectClass))
+GType mai_atk_object_get_type(void);
+GType mai_util_get_type();
+
+// This is a pointer to the atk_table_cell_get_type function if we are using
+// a version of atk that defines that.
+extern "C" GType (*gAtkTableCellGetTypeFunc)();
+
+mozilla::a11y::AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj);
+mozilla::a11y::RemoteAccessible* GetProxy(AtkObject* aAtkObj);
+mozilla::a11y::Accessible* GetInternalObj(AtkObject* aObj);
+AtkObject* GetWrapperFor(mozilla::a11y::Accessible* acc);
+
+extern int atkMajorVersion, atkMinorVersion, atkMicroVersion;
+
+/**
+ * Return true if the loaded version of libatk-1.0.so is at least
+ * aMajor.aMinor.aMicro.
+ */
+static inline bool IsAtkVersionAtLeast(int aMajor, int aMinor, int aMicro = 0) {
+ return aMajor < atkMajorVersion ||
+ (aMajor == atkMajorVersion &&
+ (aMinor < atkMinorVersion ||
+ (aMinor == atkMinorVersion && aMicro <= atkMicroVersion)));
+}
+
+/**
+ * This MaiAtkObject is a thin wrapper, in the MAI namespace, for AtkObject
+ */
+struct MaiAtkObject {
+ AtkObject parent;
+ /*
+ * The AccessibleWrap whose properties and features are exported
+ * via this object instance.
+ */
+ mozilla::a11y::Accessible* acc;
+
+ /*
+ * Get the AtkHyperlink for this atk object.
+ */
+ AtkHyperlink* GetAtkHyperlink();
+
+ /*
+ * Shutdown this AtkObject.
+ */
+ void Shutdown();
+
+ /*
+ * Notify atk of a state change on this AtkObject.
+ */
+ void FireStateChangeEvent(uint64_t aState, bool aEnabled);
+
+ /*
+ * Notify ATK of a text change within this ATK object.
+ */
+ void FireTextChangeEvent(const nsAString& aStr, int32_t aStart, uint32_t aLen,
+ bool aIsInsert, bool aIsFromUser);
+
+ /**
+ * Notify ATK of a shown or hidden subtree rooted at aObject whose parent is
+ * aParent
+ */
+ void FireAtkShowHideEvent(AtkObject* aParent, bool aIsAdded, bool aFromUser);
+
+ private:
+ /*
+ * do we have text-remove and text-insert signals if not we need to use
+ * text-changed see AccessibleWrap::FireAtkTextChangedEvent() and
+ * bug 619002
+ */
+ enum EAvailableAtkSignals {
+ eUnknown,
+ eHaveNewAtkTextSignals,
+ eNoNewAtkSignals
+ };
+
+ static EAvailableAtkSignals gAvailableAtkSignals;
+};
+
+#endif /* __NS_MAI_H__ */
diff --git a/accessible/atk/nsMaiHyperlink.cpp b/accessible/atk/nsMaiHyperlink.cpp
new file mode 100644
index 0000000000..65f223e528
--- /dev/null
+++ b/accessible/atk/nsMaiHyperlink.cpp
@@ -0,0 +1,216 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIURI.h"
+#include "nsMaiHyperlink.h"
+#include "mozilla/a11y/RemoteAccessible.h"
+
+using namespace mozilla::a11y;
+
+/* MaiAtkHyperlink */
+
+#define MAI_TYPE_ATK_HYPERLINK (mai_atk_hyperlink_get_type())
+#define MAI_ATK_HYPERLINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), MAI_TYPE_ATK_HYPERLINK, MaiAtkHyperlink))
+#define MAI_ATK_HYPERLINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), MAI_TYPE_ATK_HYPERLINK, \
+ MaiAtkHyperlinkClass))
+#define MAI_IS_ATK_HYPERLINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), MAI_TYPE_ATK_HYPERLINK))
+#define MAI_IS_ATK_HYPERLINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), MAI_TYPE_ATK_HYPERLINK))
+#define MAI_ATK_HYPERLINK_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), MAI_TYPE_ATK_HYPERLINK, \
+ MaiAtkHyperlinkClass))
+
+/**
+ * This MaiAtkHyperlink is a thin wrapper, in the MAI namespace,
+ * for AtkHyperlink
+ */
+
+struct MaiAtkHyperlink {
+ AtkHyperlink parent;
+
+ /*
+ * The MaiHyperlink whose properties and features are exported via this
+ * hyperlink instance.
+ */
+ MaiHyperlink* maiHyperlink;
+};
+
+struct MaiAtkHyperlinkClass {
+ AtkHyperlinkClass parent_class;
+};
+
+GType mai_atk_hyperlink_get_type(void);
+
+G_BEGIN_DECLS
+/* callbacks for AtkHyperlink */
+static void classInitCB(AtkHyperlinkClass* aClass);
+static void finalizeCB(GObject* aObj);
+
+/* callbacks for AtkHyperlink virtual functions */
+static gchar* getUriCB(AtkHyperlink* aLink, gint aLinkIndex);
+static AtkObject* getObjectCB(AtkHyperlink* aLink, gint aLinkIndex);
+static gint getEndIndexCB(AtkHyperlink* aLink);
+static gint getStartIndexCB(AtkHyperlink* aLink);
+static gboolean isValidCB(AtkHyperlink* aLink);
+static gint getAnchorCountCB(AtkHyperlink* aLink);
+G_END_DECLS
+
+static gpointer parent_class = nullptr;
+
+static MaiHyperlink* GetMaiHyperlink(AtkHyperlink* aHyperlink) {
+ NS_ENSURE_TRUE(MAI_IS_ATK_HYPERLINK(aHyperlink), nullptr);
+ MaiHyperlink* maiHyperlink = MAI_ATK_HYPERLINK(aHyperlink)->maiHyperlink;
+ NS_ENSURE_TRUE(maiHyperlink != nullptr, nullptr);
+ NS_ENSURE_TRUE(maiHyperlink->GetAtkHyperlink() == aHyperlink, nullptr);
+ return maiHyperlink;
+}
+
+GType mai_atk_hyperlink_get_type(void) {
+ static GType type = 0;
+
+ if (!type) {
+ static const GTypeInfo tinfo = {
+ sizeof(MaiAtkHyperlinkClass),
+ (GBaseInitFunc) nullptr,
+ (GBaseFinalizeFunc) nullptr,
+ (GClassInitFunc)classInitCB,
+ (GClassFinalizeFunc) nullptr,
+ nullptr, /* class data */
+ sizeof(MaiAtkHyperlink), /* instance size */
+ 0, /* nb preallocs */
+ (GInstanceInitFunc) nullptr,
+ nullptr /* value table */
+ };
+
+ type = g_type_register_static(ATK_TYPE_HYPERLINK, "MaiAtkHyperlink", &tinfo,
+ GTypeFlags(0));
+ }
+ return type;
+}
+
+MaiHyperlink::MaiHyperlink(Accessible* aHyperLink)
+ : mHyperlink(aHyperLink), mMaiAtkHyperlink(nullptr) {
+ mMaiAtkHyperlink = reinterpret_cast<AtkHyperlink*>(
+ g_object_new(mai_atk_hyperlink_get_type(), nullptr));
+ NS_ASSERTION(mMaiAtkHyperlink, "OUT OF MEMORY");
+ if (!mMaiAtkHyperlink) return;
+
+ MAI_ATK_HYPERLINK(mMaiAtkHyperlink)->maiHyperlink = this;
+}
+
+MaiHyperlink::~MaiHyperlink() {
+ if (mMaiAtkHyperlink) {
+ MAI_ATK_HYPERLINK(mMaiAtkHyperlink)->maiHyperlink = nullptr;
+ g_object_unref(mMaiAtkHyperlink);
+ }
+}
+
+/* static functions for ATK callbacks */
+
+void classInitCB(AtkHyperlinkClass* aClass) {
+ GObjectClass* gobject_class = G_OBJECT_CLASS(aClass);
+
+ parent_class = g_type_class_peek_parent(aClass);
+
+ aClass->get_uri = getUriCB;
+ aClass->get_object = getObjectCB;
+ aClass->get_end_index = getEndIndexCB;
+ aClass->get_start_index = getStartIndexCB;
+ aClass->is_valid = isValidCB;
+ aClass->get_n_anchors = getAnchorCountCB;
+
+ gobject_class->finalize = finalizeCB;
+}
+
+void finalizeCB(GObject* aObj) {
+ NS_ASSERTION(MAI_IS_ATK_HYPERLINK(aObj), "Invalid MaiAtkHyperlink");
+ if (!MAI_IS_ATK_HYPERLINK(aObj)) return;
+
+ MaiAtkHyperlink* maiAtkHyperlink = MAI_ATK_HYPERLINK(aObj);
+ maiAtkHyperlink->maiHyperlink = nullptr;
+
+ /* call parent finalize function */
+ if (G_OBJECT_CLASS(parent_class)->finalize) {
+ G_OBJECT_CLASS(parent_class)->finalize(aObj);
+ }
+}
+
+gchar* getUriCB(AtkHyperlink* aLink, gint aLinkIndex) {
+ MaiHyperlink* maiLink = GetMaiHyperlink(aLink);
+ if (!maiLink) {
+ return nullptr;
+ }
+
+ Accessible* acc = maiLink->Acc();
+ if (!acc) {
+ return nullptr;
+ }
+
+ nsAutoCString cautoStr;
+ nsCOMPtr<nsIURI> uri = acc->AnchorURIAt(aLinkIndex);
+ if (!uri) return nullptr;
+
+ nsresult rv = uri->GetSpec(cautoStr);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ return g_strdup(cautoStr.get());
+}
+
+AtkObject* getObjectCB(AtkHyperlink* aLink, gint aLinkIndex) {
+ MaiHyperlink* maiLink = GetMaiHyperlink(aLink);
+ if (!maiLink) {
+ return nullptr;
+ }
+
+ Accessible* acc = maiLink->Acc();
+ if (!acc) {
+ return nullptr;
+ }
+
+ Accessible* anchor = acc->AnchorAt(aLinkIndex);
+ return anchor ? GetWrapperFor(anchor) : nullptr;
+}
+
+gint getEndIndexCB(AtkHyperlink* aLink) {
+ MaiHyperlink* maiLink = GetMaiHyperlink(aLink);
+ if (!maiLink) return false;
+
+ return static_cast<gint>(maiLink->Acc()->EndOffset());
+}
+
+gint getStartIndexCB(AtkHyperlink* aLink) {
+ MaiHyperlink* maiLink = GetMaiHyperlink(aLink);
+ if (!maiLink) return -1;
+
+ return static_cast<gint>(maiLink->Acc()->StartOffset());
+}
+
+gboolean isValidCB(AtkHyperlink* aLink) {
+ MaiHyperlink* maiLink = GetMaiHyperlink(aLink);
+ if (!maiLink) return false;
+
+ Accessible* acc = maiLink->Acc();
+ if (!acc) {
+ return false;
+ }
+
+ return static_cast<gboolean>(acc->IsLinkValid());
+}
+
+gint getAnchorCountCB(AtkHyperlink* aLink) {
+ MaiHyperlink* maiLink = GetMaiHyperlink(aLink);
+ if (!maiLink) return -1;
+
+ Accessible* acc = maiLink->Acc();
+ if (!acc) {
+ return -1;
+ }
+
+ return static_cast<gint>(acc->AnchorCount());
+}
diff --git a/accessible/atk/nsMaiHyperlink.h b/accessible/atk/nsMaiHyperlink.h
new file mode 100644
index 0000000000..34f517cc7a
--- /dev/null
+++ b/accessible/atk/nsMaiHyperlink.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __MAI_HYPERLINK_H__
+#define __MAI_HYPERLINK_H__
+
+#include "nsMai.h"
+#include "mozilla/a11y/Accessible.h"
+#include "mozilla/a11y/LocalAccessible.h"
+#include "mozilla/a11y/RemoteAccessible.h"
+#include "nsDebug.h"
+
+struct _AtkHyperlink;
+typedef struct _AtkHyperlink AtkHyperlink;
+
+namespace mozilla {
+namespace a11y {
+
+/*
+ * MaiHyperlink is a auxiliary class for MaiInterfaceHyperText.
+ */
+
+class MaiHyperlink {
+ public:
+ explicit MaiHyperlink(Accessible* aHyperLink);
+ ~MaiHyperlink();
+
+ public:
+ AtkHyperlink* GetAtkHyperlink() const { return mMaiAtkHyperlink; }
+ Accessible* Acc() {
+ if (!mHyperlink) {
+ return nullptr;
+ }
+ NS_ASSERTION(mHyperlink->IsLink(), "Why isn't it a link!");
+ return mHyperlink;
+ }
+
+ protected:
+ Accessible* mHyperlink;
+ AtkHyperlink* mMaiAtkHyperlink;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif /* __MAI_HYPERLINK_H__ */
diff --git a/accessible/atk/nsMaiInterfaceAction.cpp b/accessible/atk/nsMaiInterfaceAction.cpp
new file mode 100644
index 0000000000..8149e0aff5
--- /dev/null
+++ b/accessible/atk/nsMaiInterfaceAction.cpp
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InterfaceInitFuncs.h"
+
+#include "LocalAccessible-inl.h"
+#include "nsMai.h"
+#include "mozilla/Likely.h"
+#include "nsAccessibilityService.h"
+#include "RemoteAccessible.h"
+#include "nsString.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+extern "C" {
+
+static gboolean doActionCB(AtkAction* aAction, gint aActionIndex) {
+ AtkObject* atkObject = ATK_OBJECT(aAction);
+ if (Accessible* acc = GetInternalObj(atkObject)) {
+ return acc->DoAction(aActionIndex);
+ }
+
+ return false;
+}
+
+static gint getActionCountCB(AtkAction* aAction) {
+ AtkObject* atkObject = ATK_OBJECT(aAction);
+ if (Accessible* acc = GetInternalObj(atkObject)) {
+ return acc->ActionCount();
+ }
+
+ return 0;
+}
+
+static const gchar* getActionDescriptionCB(AtkAction* aAction,
+ gint aActionIndex) {
+ AtkObject* atkObject = ATK_OBJECT(aAction);
+ nsAutoString description;
+ if (Accessible* acc = GetInternalObj(atkObject)) {
+ acc->ActionDescriptionAt(aActionIndex, description);
+ return AccessibleWrap::ReturnString(description);
+ }
+
+ return nullptr;
+}
+
+static const gchar* getActionNameCB(AtkAction* aAction, gint aActionIndex) {
+ AtkObject* atkObject = ATK_OBJECT(aAction);
+ nsAutoString autoStr;
+ if (Accessible* acc = GetInternalObj(atkObject)) {
+ acc->ActionNameAt(aActionIndex, autoStr);
+ return AccessibleWrap::ReturnString(autoStr);
+ }
+
+ return nullptr;
+}
+
+static const gchar* getKeyBindingCB(AtkAction* aAction, gint aActionIndex) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aAction));
+ if (!acc) {
+ return nullptr;
+ }
+ nsAutoString keyBindingsStr;
+ AccessibleWrap::GetKeyBinding(acc, keyBindingsStr);
+
+ return AccessibleWrap::ReturnString(keyBindingsStr);
+}
+}
+
+void actionInterfaceInitCB(AtkActionIface* aIface) {
+ NS_ASSERTION(aIface, "Invalid aIface");
+ if (MOZ_UNLIKELY(!aIface)) return;
+
+ aIface->do_action = doActionCB;
+ aIface->get_n_actions = getActionCountCB;
+ aIface->get_description = getActionDescriptionCB;
+ aIface->get_keybinding = getKeyBindingCB;
+ aIface->get_name = getActionNameCB;
+}
diff --git a/accessible/atk/nsMaiInterfaceComponent.cpp b/accessible/atk/nsMaiInterfaceComponent.cpp
new file mode 100644
index 0000000000..c2da38fa22
--- /dev/null
+++ b/accessible/atk/nsMaiInterfaceComponent.cpp
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InterfaceInitFuncs.h"
+
+#include "LocalAccessible-inl.h"
+#include "AccessibleWrap.h"
+#include "nsAccUtils.h"
+#include "nsMai.h"
+#include "mozilla/Likely.h"
+#include "mozilla/a11y/DocAccessibleParent.h"
+#include "mozilla/a11y/RemoteAccessible.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "nsAccessibilityService.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+extern "C" {
+
+static AtkObject* refAccessibleAtPointCB(AtkComponent* aComponent, gint aAccX,
+ gint aAccY, AtkCoordType aCoordType) {
+ return refAccessibleAtPointHelper(ATK_OBJECT(aComponent), aAccX, aAccY,
+ aCoordType);
+}
+
+static void getExtentsCB(AtkComponent* aComponent, gint* aX, gint* aY,
+ gint* aWidth, gint* aHeight, AtkCoordType aCoordType) {
+ getExtentsHelper(ATK_OBJECT(aComponent), aX, aY, aWidth, aHeight, aCoordType);
+}
+
+static gboolean grabFocusCB(AtkComponent* aComponent) {
+ AtkObject* atkObject = ATK_OBJECT(aComponent);
+ Accessible* acc = GetInternalObj(atkObject);
+ if (acc) {
+ acc->TakeFocus();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// ScrollType is compatible
+MOZ_CAN_RUN_SCRIPT_BOUNDARY
+static gboolean scrollToCB(AtkComponent* aComponent, AtkScrollType type) {
+ AtkObject* atkObject = ATK_OBJECT(aComponent);
+ if (Accessible* acc = GetInternalObj(atkObject)) {
+ acc->ScrollTo(type);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+// CoordType is compatible
+static gboolean scrollToPointCB(AtkComponent* aComponent, AtkCoordType coords,
+ gint x, gint y) {
+ AtkObject* atkObject = ATK_OBJECT(aComponent);
+ AccessibleWrap* accWrap = GetAccessibleWrap(atkObject);
+ if (accWrap) {
+ accWrap->ScrollToPoint(coords, x, y);
+ return TRUE;
+ }
+
+ RemoteAccessible* proxy = GetProxy(atkObject);
+ if (proxy) {
+ proxy->ScrollToPoint(coords, x, y);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+}
+
+AtkObject* refAccessibleAtPointHelper(AtkObject* aAtkObj, gint aX, gint aY,
+ AtkCoordType aCoordType) {
+ Accessible* acc = GetInternalObj(aAtkObj);
+ if (!acc) {
+ return nullptr;
+ }
+
+ // Accessible::ChildAtPoint(x,y) is in screen pixels.
+ if (aCoordType == ATK_XY_WINDOW) {
+ mozilla::LayoutDeviceIntPoint winCoords =
+ nsAccUtils::GetScreenCoordsForWindow(acc);
+ aX += winCoords.x;
+ aY += winCoords.y;
+ }
+
+ Accessible* accAtPoint =
+ acc->ChildAtPoint(aX, aY, Accessible::EWhichChildAtPoint::DeepestChild);
+ if (!accAtPoint) {
+ return nullptr;
+ }
+ roles::Role role = accAtPoint->Role();
+ if (role == roles::TEXT_LEAF || role == roles::STATICTEXT) {
+ // We don't include text leaf nodes in the ATK tree, so return the parent.
+ accAtPoint = accAtPoint->Parent();
+ MOZ_ASSERT(accAtPoint, "Text leaf should always have a parent");
+ }
+ AtkObject* atkObj = GetWrapperFor(accAtPoint);
+ if (atkObj) {
+ g_object_ref(atkObj);
+ }
+ return atkObj;
+}
+
+void getExtentsHelper(AtkObject* aAtkObj, gint* aX, gint* aY, gint* aWidth,
+ gint* aHeight, AtkCoordType aCoordType) {
+ *aX = *aY = *aWidth = *aHeight = -1;
+
+ Accessible* acc = GetInternalObj(aAtkObj);
+ if (!acc) {
+ return;
+ }
+
+ mozilla::LayoutDeviceIntRect screenRect = acc->Bounds();
+ if (screenRect.IsEmpty()) {
+ return;
+ }
+
+ if (aCoordType == ATK_XY_WINDOW) {
+ mozilla::LayoutDeviceIntPoint winCoords =
+ nsAccUtils::GetScreenCoordsForWindow(acc);
+ screenRect.x -= winCoords.x;
+ screenRect.y -= winCoords.y;
+ }
+
+ *aX = screenRect.x;
+ *aY = screenRect.y;
+ *aWidth = screenRect.width;
+ *aHeight = screenRect.height;
+}
+
+void componentInterfaceInitCB(AtkComponentIface* aIface) {
+ NS_ASSERTION(aIface, "Invalid Interface");
+ if (MOZ_UNLIKELY(!aIface)) return;
+
+ /*
+ * Use default implementation in atk for contains, get_position,
+ * and get_size
+ */
+ aIface->ref_accessible_at_point = refAccessibleAtPointCB;
+ aIface->get_extents = getExtentsCB;
+ aIface->grab_focus = grabFocusCB;
+ if (IsAtkVersionAtLeast(2, 30)) {
+ aIface->scroll_to = scrollToCB;
+ aIface->scroll_to_point = scrollToPointCB;
+ }
+}
diff --git a/accessible/atk/nsMaiInterfaceDocument.cpp b/accessible/atk/nsMaiInterfaceDocument.cpp
new file mode 100644
index 0000000000..da1bffce37
--- /dev/null
+++ b/accessible/atk/nsMaiInterfaceDocument.cpp
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InterfaceInitFuncs.h"
+
+#include "LocalAccessible-inl.h"
+#include "AccessibleWrap.h"
+#include "DocAccessible.h"
+#include "nsAccUtils.h"
+#include "nsMai.h"
+#include "RemoteAccessible.h"
+#include "mozilla/a11y/DocAccessibleParent.h"
+#include "mozilla/Likely.h"
+
+using namespace mozilla::a11y;
+
+static const char* const kDocUrlName = "DocURL";
+static const char* const kMimeTypeName = "MimeType";
+
+// below functions are vfuncs on an ATK interface so they need to be C call
+extern "C" {
+
+static const gchar* getDocumentLocaleCB(AtkDocument* aDocument);
+static AtkAttributeSet* getDocumentAttributesCB(AtkDocument* aDocument);
+static const gchar* getDocumentAttributeValueCB(AtkDocument* aDocument,
+ const gchar* aAttrName);
+
+void documentInterfaceInitCB(AtkDocumentIface* aIface) {
+ NS_ASSERTION(aIface, "Invalid Interface");
+ if (MOZ_UNLIKELY(!aIface)) return;
+
+ /*
+ * We don't support get_document or set_attribute right now.
+ */
+ aIface->get_document_attributes = getDocumentAttributesCB;
+ aIface->get_document_attribute_value = getDocumentAttributeValueCB;
+ aIface->get_document_locale = getDocumentLocaleCB;
+}
+
+const gchar* getDocumentLocaleCB(AtkDocument* aDocument) {
+ nsAutoString locale;
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aDocument));
+ if (acc) {
+ acc->Language(locale);
+ }
+
+ return locale.IsEmpty() ? nullptr : AccessibleWrap::ReturnString(locale);
+}
+
+static inline GSList* prependToList(GSList* aList, const char* const aName,
+ const nsAutoString& aValue) {
+ if (aValue.IsEmpty()) {
+ return aList;
+ }
+
+ // libspi will free these
+ AtkAttribute* atkAttr = (AtkAttribute*)g_malloc(sizeof(AtkAttribute));
+ atkAttr->name = g_strdup(aName);
+ atkAttr->value = g_strdup(NS_ConvertUTF16toUTF8(aValue).get());
+ return g_slist_prepend(aList, atkAttr);
+}
+
+AtkAttributeSet* getDocumentAttributesCB(AtkDocument* aDocument) {
+ nsAutoString url;
+ nsAutoString mimeType;
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aDocument));
+
+ if (!acc || !acc->IsDoc()) {
+ return nullptr;
+ }
+
+ nsAccUtils::DocumentURL(acc, url);
+ nsAccUtils::DocumentMimeType(acc, mimeType);
+
+ // according to atkobject.h, AtkAttributeSet is a GSList
+ GSList* attributes = nullptr;
+ attributes = prependToList(attributes, kDocUrlName, url);
+ attributes = prependToList(attributes, kMimeTypeName, mimeType);
+
+ return attributes;
+}
+
+const gchar* getDocumentAttributeValueCB(AtkDocument* aDocument,
+ const gchar* aAttrName) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aDocument));
+
+ if (!acc || !acc->IsDoc()) {
+ return nullptr;
+ }
+
+ nsAutoString attrValue;
+ if (!strcasecmp(aAttrName, kDocUrlName)) {
+ nsAccUtils::DocumentURL(acc, attrValue);
+ } else if (!strcasecmp(aAttrName, kMimeTypeName)) {
+ nsAccUtils::DocumentMimeType(acc, attrValue);
+ } else {
+ return nullptr;
+ }
+
+ return attrValue.IsEmpty() ? nullptr
+ : AccessibleWrap::ReturnString(attrValue);
+}
+}
diff --git a/accessible/atk/nsMaiInterfaceEditableText.cpp b/accessible/atk/nsMaiInterfaceEditableText.cpp
new file mode 100644
index 0000000000..2dc692362e
--- /dev/null
+++ b/accessible/atk/nsMaiInterfaceEditableText.cpp
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InterfaceInitFuncs.h"
+
+#include "LocalAccessible-inl.h"
+#include "HyperTextAccessible-inl.h"
+#include "nsMai.h"
+#include "RemoteAccessible.h"
+#include "nsString.h"
+#include "mozilla/Likely.h"
+
+using namespace mozilla::a11y;
+
+extern "C" {
+static void setTextContentsCB(AtkEditableText* aText, const gchar* aString) {
+ if (Accessible* acc = GetInternalObj(ATK_OBJECT(aText))) {
+ if (acc->IsTextRole()) {
+ return;
+ }
+ if (HyperTextAccessibleBase* text = acc->AsHyperTextBase()) {
+ NS_ConvertUTF8toUTF16 strContent(aString);
+ text->ReplaceText(strContent);
+ }
+ }
+}
+
+static void insertTextCB(AtkEditableText* aText, const gchar* aString,
+ gint aLength, gint* aPosition) {
+ if (Accessible* acc = GetInternalObj(ATK_OBJECT(aText))) {
+ if (acc->IsTextRole()) {
+ return;
+ }
+ if (HyperTextAccessibleBase* text = acc->AsHyperTextBase()) {
+ NS_ConvertUTF8toUTF16 strContent(aString);
+ text->InsertText(strContent, *aPosition);
+ }
+ }
+}
+
+static void copyTextCB(AtkEditableText* aText, gint aStartPos, gint aEndPos) {
+ if (Accessible* acc = GetInternalObj(ATK_OBJECT(aText))) {
+ if (acc->IsTextRole()) {
+ return;
+ }
+ if (HyperTextAccessibleBase* text = acc->AsHyperTextBase()) {
+ text->CopyText(aStartPos, aEndPos);
+ }
+ }
+}
+
+static void cutTextCB(AtkEditableText* aText, gint aStartPos, gint aEndPos) {
+ if (Accessible* acc = GetInternalObj(ATK_OBJECT(aText))) {
+ if (acc->IsTextRole()) {
+ return;
+ }
+ if (HyperTextAccessibleBase* text = acc->AsHyperTextBase()) {
+ text->CutText(aStartPos, aEndPos);
+ }
+ }
+}
+
+static void deleteTextCB(AtkEditableText* aText, gint aStartPos, gint aEndPos) {
+ if (Accessible* acc = GetInternalObj(ATK_OBJECT(aText))) {
+ if (acc->IsTextRole()) {
+ return;
+ }
+ if (HyperTextAccessibleBase* text = acc->AsHyperTextBase()) {
+ text->DeleteText(aStartPos, aEndPos);
+ }
+ }
+}
+
+MOZ_CAN_RUN_SCRIPT_BOUNDARY
+static void pasteTextCB(AtkEditableText* aText, gint aPosition) {
+ if (Accessible* acc = GetInternalObj(ATK_OBJECT(aText))) {
+ if (acc->IsTextRole()) {
+ return;
+ }
+ if (HyperTextAccessibleBase* text = acc->AsHyperTextBase()) {
+ text->PasteText(aPosition);
+ }
+ }
+}
+}
+
+void editableTextInterfaceInitCB(AtkEditableTextIface* aIface) {
+ NS_ASSERTION(aIface, "Invalid aIface");
+ if (MOZ_UNLIKELY(!aIface)) return;
+
+ aIface->set_text_contents = setTextContentsCB;
+ aIface->insert_text = insertTextCB;
+ aIface->copy_text = copyTextCB;
+ aIface->cut_text = cutTextCB;
+ aIface->delete_text = deleteTextCB;
+ aIface->paste_text = pasteTextCB;
+}
diff --git a/accessible/atk/nsMaiInterfaceHyperlinkImpl.cpp b/accessible/atk/nsMaiInterfaceHyperlinkImpl.cpp
new file mode 100644
index 0000000000..ed8c4f4fce
--- /dev/null
+++ b/accessible/atk/nsMaiInterfaceHyperlinkImpl.cpp
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InterfaceInitFuncs.h"
+
+#include "nsMaiHyperlink.h"
+#include "mozilla/Likely.h"
+
+using namespace mozilla::a11y;
+
+extern "C" {
+static AtkHyperlink* getHyperlinkCB(AtkHyperlinkImpl* aImpl) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aImpl));
+ if (!acc) {
+ return nullptr;
+ }
+
+ NS_ASSERTION(acc->IsLink(), "why isn't it a link!");
+
+ return MAI_ATK_OBJECT(aImpl)->GetAtkHyperlink();
+}
+}
+
+void hyperlinkImplInterfaceInitCB(AtkHyperlinkImplIface* aIface) {
+ NS_ASSERTION(aIface, "no interface!");
+ if (MOZ_UNLIKELY(!aIface)) return;
+
+ aIface->get_hyperlink = getHyperlinkCB;
+}
diff --git a/accessible/atk/nsMaiInterfaceHypertext.cpp b/accessible/atk/nsMaiInterfaceHypertext.cpp
new file mode 100644
index 0000000000..1e073c87d2
--- /dev/null
+++ b/accessible/atk/nsMaiInterfaceHypertext.cpp
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InterfaceInitFuncs.h"
+
+#include "LocalAccessible-inl.h"
+#include "HyperTextAccessible.h"
+#include "nsMai.h"
+#include "nsMaiHyperlink.h"
+#include "RemoteAccessible.h"
+#include "mozilla/Likely.h"
+
+using namespace mozilla::a11y;
+
+extern "C" {
+
+static AtkHyperlink* getLinkCB(AtkHypertext* aText, gint aLinkIndex) {
+ if (Accessible* acc = GetInternalObj(ATK_OBJECT(aText))) {
+ if (HyperTextAccessibleBase* hyperText = acc->AsHyperTextBase()) {
+ Accessible* linkAcc = hyperText->LinkAt(aLinkIndex);
+ AtkObject* atkHyperLink = GetWrapperFor(linkAcc);
+ NS_ENSURE_TRUE(IS_MAI_OBJECT(atkHyperLink), nullptr);
+ return MAI_ATK_OBJECT(atkHyperLink)->GetAtkHyperlink();
+ }
+ }
+
+ return nullptr;
+}
+
+static gint getLinkCountCB(AtkHypertext* aText) {
+ if (Accessible* acc = GetInternalObj(ATK_OBJECT(aText))) {
+ if (HyperTextAccessibleBase* hyperText = acc->AsHyperTextBase()) {
+ return static_cast<gint>(hyperText->LinkCount());
+ }
+ }
+ return -1;
+}
+
+static gint getLinkIndexCB(AtkHypertext* aText, gint aCharIndex) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
+ if (!acc) {
+ return -1;
+ }
+ HyperTextAccessibleBase* hyperText = acc->AsHyperTextBase();
+ if (!hyperText) {
+ return -1;
+ }
+ return hyperText->LinkIndexAtOffset(aCharIndex);
+}
+
+} // extern "C"
+
+void hypertextInterfaceInitCB(AtkHypertextIface* aIface) {
+ NS_ASSERTION(aIface, "no interface!");
+ if (MOZ_UNLIKELY(!aIface)) return;
+
+ aIface->get_link = getLinkCB;
+ aIface->get_n_links = getLinkCountCB;
+ aIface->get_link_index = getLinkIndexCB;
+}
diff --git a/accessible/atk/nsMaiInterfaceImage.cpp b/accessible/atk/nsMaiInterfaceImage.cpp
new file mode 100644
index 0000000000..dee28f109f
--- /dev/null
+++ b/accessible/atk/nsMaiInterfaceImage.cpp
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InterfaceInitFuncs.h"
+
+#include "AccessibleWrap.h"
+#include "mozilla/a11y/Accessible.h"
+#include "mozilla/Likely.h"
+#include "nsMai.h"
+#include "nsIAccessibleTypes.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+extern "C" {
+const gchar* getDescriptionCB(AtkObject* aAtkObj);
+
+static void getImagePositionCB(AtkImage* aImage, gint* aAccX, gint* aAccY,
+ AtkCoordType aCoordType) {
+ LayoutDeviceIntPoint pos(-1, -1);
+ uint32_t geckoCoordType =
+ (aCoordType == ATK_XY_WINDOW)
+ ? nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE
+ : nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE;
+
+ if (Accessible* acc = GetInternalObj(ATK_OBJECT(aImage))) {
+ pos = acc->Position(geckoCoordType);
+ }
+
+ *aAccX = pos.x;
+ *aAccY = pos.y;
+}
+
+static const gchar* getImageDescriptionCB(AtkImage* aImage) {
+ return getDescriptionCB(ATK_OBJECT(aImage));
+}
+
+static void getImageSizeCB(AtkImage* aImage, gint* aAccWidth,
+ gint* aAccHeight) {
+ LayoutDeviceIntSize size(-1, -1);
+ if (Accessible* acc = GetInternalObj(ATK_OBJECT(aImage))) {
+ size = acc->Size();
+ }
+
+ *aAccWidth = size.width;
+ *aAccHeight = size.height;
+}
+
+} // extern "C"
+
+void imageInterfaceInitCB(AtkImageIface* aIface) {
+ NS_ASSERTION(aIface, "no interface!");
+ if (MOZ_UNLIKELY(!aIface)) return;
+
+ aIface->get_image_position = getImagePositionCB;
+ aIface->get_image_description = getImageDescriptionCB;
+ aIface->get_image_size = getImageSizeCB;
+}
diff --git a/accessible/atk/nsMaiInterfaceSelection.cpp b/accessible/atk/nsMaiInterfaceSelection.cpp
new file mode 100644
index 0000000000..80b4d260f1
--- /dev/null
+++ b/accessible/atk/nsMaiInterfaceSelection.cpp
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InterfaceInitFuncs.h"
+
+#include "LocalAccessible-inl.h"
+#include "AccessibleWrap.h"
+#include "nsMai.h"
+#include "mozilla/Likely.h"
+
+#include <atk/atkobject.h>
+#include <atk/atkselection.h>
+
+using namespace mozilla::a11y;
+
+extern "C" {
+
+static gboolean addSelectionCB(AtkSelection* aSelection, gint i) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aSelection));
+ if (acc && acc->IsSelect()) {
+ return acc->AddItemToSelection(i);
+ }
+
+ return FALSE;
+}
+
+static gboolean clearSelectionCB(AtkSelection* aSelection) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aSelection));
+ if (acc && acc->IsSelect()) {
+ return acc->UnselectAll();
+ }
+
+ return FALSE;
+}
+
+static AtkObject* refSelectionCB(AtkSelection* aSelection, gint i) {
+ AtkObject* atkObj = nullptr;
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aSelection));
+ Accessible* selectedItem = acc->GetSelectedItem(i);
+ if (selectedItem) {
+ atkObj = GetWrapperFor(selectedItem);
+ }
+
+ if (atkObj) {
+ g_object_ref(atkObj);
+ }
+
+ return atkObj;
+}
+
+static gint getSelectionCountCB(AtkSelection* aSelection) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aSelection));
+ if (acc && acc->IsSelect()) {
+ return acc->SelectedItemCount();
+ }
+
+ return -1;
+}
+
+static gboolean isChildSelectedCB(AtkSelection* aSelection, gint i) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aSelection));
+ if (acc && acc->IsSelect()) {
+ return acc->IsItemSelected(i);
+ }
+
+ return FALSE;
+}
+
+static gboolean removeSelectionCB(AtkSelection* aSelection, gint i) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aSelection));
+ if (acc && acc->IsSelect()) {
+ return acc->RemoveItemFromSelection(i);
+ }
+
+ return FALSE;
+}
+
+static gboolean selectAllSelectionCB(AtkSelection* aSelection) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aSelection));
+ if (acc && acc->IsSelect()) {
+ return acc->SelectAll();
+ }
+
+ return FALSE;
+}
+}
+
+void selectionInterfaceInitCB(AtkSelectionIface* aIface) {
+ NS_ASSERTION(aIface, "Invalid aIface");
+ if (MOZ_UNLIKELY(!aIface)) return;
+
+ aIface->add_selection = addSelectionCB;
+ aIface->clear_selection = clearSelectionCB;
+ aIface->ref_selection = refSelectionCB;
+ aIface->get_selection_count = getSelectionCountCB;
+ aIface->is_child_selected = isChildSelectedCB;
+ aIface->remove_selection = removeSelectionCB;
+ aIface->select_all_selection = selectAllSelectionCB;
+}
diff --git a/accessible/atk/nsMaiInterfaceTable.cpp b/accessible/atk/nsMaiInterfaceTable.cpp
new file mode 100644
index 0000000000..cfba9e78d1
--- /dev/null
+++ b/accessible/atk/nsMaiInterfaceTable.cpp
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InterfaceInitFuncs.h"
+
+#include "AccessibleWrap.h"
+#include "mozilla/a11y/TableAccessible.h"
+#include "nsAccessibilityService.h"
+#include "nsMai.h"
+#include "RemoteAccessible.h"
+#include "nsTArray.h"
+
+#include "mozilla/Likely.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+extern "C" {
+static AtkObject* refAtCB(AtkTable* aTable, gint aRowIdx, gint aColIdx) {
+ if (aRowIdx < 0 || aColIdx < 0) {
+ return nullptr;
+ }
+
+ AtkObject* cellAtkObj = nullptr;
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aTable));
+ if (!acc) {
+ return nullptr;
+ }
+ Accessible* cell = acc->AsTable()->CellAt(aRowIdx, aColIdx);
+ if (!cell) {
+ return nullptr;
+ }
+
+ cellAtkObj = GetWrapperFor(cell);
+
+ if (cellAtkObj) {
+ g_object_ref(cellAtkObj);
+ }
+
+ return cellAtkObj;
+}
+
+static gint getIndexAtCB(AtkTable* aTable, gint aRowIdx, gint aColIdx) {
+ if (aRowIdx < 0 || aColIdx < 0) {
+ return -1;
+ }
+
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aTable));
+ if (!acc) {
+ return -1;
+ }
+ return static_cast<gint>(acc->AsTable()->CellIndexAt(aRowIdx, aColIdx));
+}
+
+static gint getColumnAtIndexCB(AtkTable* aTable, gint aIdx) {
+ if (aIdx < 0) {
+ return -1;
+ }
+
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aTable));
+ if (!acc) {
+ return -1;
+ }
+ return static_cast<gint>(acc->AsTable()->ColIndexAt(aIdx));
+}
+
+static gint getRowAtIndexCB(AtkTable* aTable, gint aIdx) {
+ if (aIdx < 0) {
+ return -1;
+ }
+
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aTable));
+ if (!acc) {
+ return -1;
+ }
+ return static_cast<gint>(acc->AsTable()->RowIndexAt(aIdx));
+}
+
+static gint getColumnCountCB(AtkTable* aTable) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aTable));
+ if (!acc) {
+ return -1;
+ }
+ return static_cast<gint>(acc->AsTable()->ColCount());
+}
+
+static gint getRowCountCB(AtkTable* aTable) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aTable));
+ if (!acc) {
+ return -1;
+ }
+ return static_cast<gint>(acc->AsTable()->RowCount());
+}
+
+static gint getColumnExtentAtCB(AtkTable* aTable, gint aRowIdx, gint aColIdx) {
+ if (aRowIdx < 0 || aColIdx < 0) {
+ return -1;
+ }
+
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aTable));
+ if (!acc) {
+ return -1;
+ }
+ return static_cast<gint>(acc->AsTable()->ColExtentAt(aRowIdx, aColIdx));
+}
+
+static gint getRowExtentAtCB(AtkTable* aTable, gint aRowIdx, gint aColIdx) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aTable));
+ if (!acc) {
+ return -1;
+ }
+ return static_cast<gint>(acc->AsTable()->RowExtentAt(aRowIdx, aColIdx));
+}
+
+static AtkObject* getCaptionCB(AtkTable* aTable) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aTable));
+ if (!acc) {
+ return nullptr;
+ }
+ Accessible* caption = acc->AsTable()->Caption();
+ return caption ? GetWrapperFor(caption) : nullptr;
+}
+
+static const gchar* getColumnDescriptionCB(AtkTable* aTable, gint aColumn) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aTable));
+ if (!acc) {
+ return nullptr;
+ }
+ nsAutoString autoStr;
+ acc->AsTable()->ColDescription(aColumn, autoStr);
+ return AccessibleWrap::ReturnString(autoStr);
+}
+
+static AtkObject* getColumnHeaderCB(AtkTable* aTable, gint aColIdx) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aTable));
+ if (!acc) {
+ return nullptr;
+ }
+ Accessible* header = AccessibleWrap::GetColumnHeader(acc->AsTable(), aColIdx);
+ return header ? GetWrapperFor(header) : nullptr;
+}
+
+static const gchar* getRowDescriptionCB(AtkTable* aTable, gint aRow) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aTable));
+ if (!acc) {
+ return nullptr;
+ }
+ nsAutoString autoStr;
+ acc->AsTable()->RowDescription(aRow, autoStr);
+ return AccessibleWrap::ReturnString(autoStr);
+}
+
+static AtkObject* getRowHeaderCB(AtkTable* aTable, gint aRowIdx) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aTable));
+ if (!acc) {
+ return nullptr;
+ }
+ Accessible* header = AccessibleWrap::GetRowHeader(acc->AsTable(), aRowIdx);
+ return header ? GetWrapperFor(header) : nullptr;
+}
+
+static AtkObject* getSummaryCB(AtkTable* aTable) {
+ // Neither html:table nor xul:tree nor ARIA grid/tree have an ability to
+ // link an accessible object to specify a summary. There is closes method
+ // in TableAccessible::summary to get a summary as a string which is not
+ // mapped directly to ATK.
+ return nullptr;
+}
+
+static gint getSelectedColumnsCB(AtkTable* aTable, gint** aSelected) {
+ *aSelected = nullptr;
+
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aTable));
+ if (!acc) {
+ return 0;
+ }
+ AutoTArray<uint32_t, 10> cols;
+ acc->AsTable()->SelectedColIndices(&cols);
+
+ if (cols.IsEmpty()) return 0;
+
+ gint* atkColumns = g_new(gint, cols.Length());
+ if (!atkColumns) {
+ NS_WARNING("OUT OF MEMORY");
+ return 0;
+ }
+
+ memcpy(atkColumns, cols.Elements(), cols.Length() * sizeof(uint32_t));
+ *aSelected = atkColumns;
+ return cols.Length();
+}
+
+static gint getSelectedRowsCB(AtkTable* aTable, gint** aSelected) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aTable));
+ if (!acc) {
+ return 0;
+ }
+ AutoTArray<uint32_t, 10> rows;
+ acc->AsTable()->SelectedRowIndices(&rows);
+
+ gint* atkRows = g_new(gint, rows.Length());
+ if (!atkRows) {
+ NS_WARNING("OUT OF MEMORY");
+ return 0;
+ }
+
+ memcpy(atkRows, rows.Elements(), rows.Length() * sizeof(uint32_t));
+ *aSelected = atkRows;
+ return rows.Length();
+}
+
+static gboolean isColumnSelectedCB(AtkTable* aTable, gint aColIdx) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aTable));
+ if (!acc) {
+ return FALSE;
+ }
+ return static_cast<gboolean>(acc->AsTable()->IsColSelected(aColIdx));
+}
+
+static gboolean isRowSelectedCB(AtkTable* aTable, gint aRowIdx) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aTable));
+ if (!acc) {
+ return FALSE;
+ }
+ return static_cast<gboolean>(acc->AsTable()->IsRowSelected(aRowIdx));
+}
+
+static gboolean isCellSelectedCB(AtkTable* aTable, gint aRowIdx, gint aColIdx) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aTable));
+ if (!acc) {
+ return FALSE;
+ }
+ return static_cast<gboolean>(
+ acc->AsTable()->IsCellSelected(aRowIdx, aColIdx));
+}
+}
+
+void tableInterfaceInitCB(AtkTableIface* aIface) {
+ NS_ASSERTION(aIface, "no interface!");
+ if (MOZ_UNLIKELY(!aIface)) return;
+
+ aIface->ref_at = refAtCB;
+ aIface->get_index_at = getIndexAtCB;
+ aIface->get_column_at_index = getColumnAtIndexCB;
+ aIface->get_row_at_index = getRowAtIndexCB;
+ aIface->get_n_columns = getColumnCountCB;
+ aIface->get_n_rows = getRowCountCB;
+ aIface->get_column_extent_at = getColumnExtentAtCB;
+ aIface->get_row_extent_at = getRowExtentAtCB;
+ aIface->get_caption = getCaptionCB;
+ aIface->get_column_description = getColumnDescriptionCB;
+ aIface->get_column_header = getColumnHeaderCB;
+ aIface->get_row_description = getRowDescriptionCB;
+ aIface->get_row_header = getRowHeaderCB;
+ aIface->get_summary = getSummaryCB;
+ aIface->get_selected_columns = getSelectedColumnsCB;
+ aIface->get_selected_rows = getSelectedRowsCB;
+ aIface->is_column_selected = isColumnSelectedCB;
+ aIface->is_row_selected = isRowSelectedCB;
+ aIface->is_selected = isCellSelectedCB;
+}
diff --git a/accessible/atk/nsMaiInterfaceTableCell.cpp b/accessible/atk/nsMaiInterfaceTableCell.cpp
new file mode 100644
index 0000000000..06a684a87d
--- /dev/null
+++ b/accessible/atk/nsMaiInterfaceTableCell.cpp
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InterfaceInitFuncs.h"
+
+#include "mozilla/a11y/TableAccessible.h"
+#include "mozilla/a11y/TableCellAccessible.h"
+#include "nsAccessibilityService.h"
+#include "nsMai.h"
+#include "RemoteAccessible.h"
+#include "nsTArray.h"
+
+#include "mozilla/Likely.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+extern "C" {
+static gint GetColumnSpanCB(AtkTableCell* aCell) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aCell));
+ if (!acc) {
+ return 0;
+ }
+ return static_cast<gint>(acc->AsTableCell()->ColExtent());
+}
+
+static gint GetRowSpanCB(AtkTableCell* aCell) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aCell));
+ if (!acc) {
+ return 0;
+ }
+ return static_cast<gint>(acc->AsTableCell()->RowExtent());
+}
+
+static gboolean GetPositionCB(AtkTableCell* aCell, gint* aRow, gint* aCol) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aCell));
+ if (!acc) {
+ return false;
+ }
+ TableCellAccessible* cell = acc->AsTableCell();
+ if (!cell) {
+ return false;
+ }
+ *aRow = static_cast<gint>(cell->RowIdx());
+ *aCol = static_cast<gint>(cell->ColIdx());
+ return true;
+}
+
+static gboolean GetColumnRowSpanCB(AtkTableCell* aCell, gint* aCol, gint* aRow,
+ gint* aColExtent, gint* aRowExtent) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aCell));
+ if (!acc) {
+ return false;
+ }
+ TableCellAccessible* cellAcc = acc->AsTableCell();
+ if (!cellAcc) {
+ return false;
+ }
+ *aCol = static_cast<gint>(cellAcc->ColIdx());
+ *aRow = static_cast<gint>(cellAcc->RowIdx());
+ *aColExtent = static_cast<gint>(cellAcc->ColExtent());
+ *aRowExtent = static_cast<gint>(cellAcc->ColExtent());
+ return true;
+}
+
+static AtkObject* GetTableCB(AtkTableCell* aTableCell) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aTableCell));
+ if (!acc) {
+ return nullptr;
+ }
+ TableCellAccessible* cell = acc->AsTableCell();
+ if (!cell) {
+ return nullptr;
+ }
+ TableAccessible* table = cell->Table();
+ if (!table) {
+ return nullptr;
+ }
+ Accessible* tableAcc = table->AsAccessible();
+ return tableAcc ? GetWrapperFor(tableAcc) : nullptr;
+}
+
+static GPtrArray* GetColumnHeaderCellsCB(AtkTableCell* aCell) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aCell));
+ if (!acc) {
+ return nullptr;
+ }
+ TableCellAccessible* cell = acc->AsTableCell();
+ if (!cell) {
+ return nullptr;
+ }
+ AutoTArray<Accessible*, 10> headers;
+ cell->ColHeaderCells(&headers);
+ if (headers.IsEmpty()) {
+ return nullptr;
+ }
+
+ GPtrArray* atkHeaders = g_ptr_array_sized_new(headers.Length());
+ for (Accessible* header : headers) {
+ AtkObject* atkHeader = GetWrapperFor(header);
+ g_object_ref(atkHeader);
+ g_ptr_array_add(atkHeaders, atkHeader);
+ }
+
+ return atkHeaders;
+}
+
+static GPtrArray* GetRowHeaderCellsCB(AtkTableCell* aCell) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aCell));
+ if (!acc) {
+ return nullptr;
+ }
+ TableCellAccessible* cell = acc->AsTableCell();
+ if (!cell) {
+ return nullptr;
+ }
+ AutoTArray<Accessible*, 10> headers;
+ cell->RowHeaderCells(&headers);
+ if (headers.IsEmpty()) {
+ return nullptr;
+ }
+
+ GPtrArray* atkHeaders = g_ptr_array_sized_new(headers.Length());
+ for (Accessible* header : headers) {
+ AtkObject* atkHeader = GetWrapperFor(header);
+ g_object_ref(atkHeader);
+ g_ptr_array_add(atkHeaders, atkHeader);
+ }
+
+ return atkHeaders;
+}
+}
+
+void tableCellInterfaceInitCB(AtkTableCellIface* aIface) {
+ NS_ASSERTION(aIface, "no interface!");
+ if (MOZ_UNLIKELY(!aIface)) return;
+
+ aIface->get_column_span = GetColumnSpanCB;
+ aIface->get_column_header_cells = GetColumnHeaderCellsCB;
+ aIface->get_position = GetPositionCB;
+ aIface->get_row_span = GetRowSpanCB;
+ aIface->get_row_header_cells = GetRowHeaderCellsCB;
+ aIface->get_row_column_span = GetColumnRowSpanCB;
+ aIface->get_table = GetTableCB;
+}
diff --git a/accessible/atk/nsMaiInterfaceText.cpp b/accessible/atk/nsMaiInterfaceText.cpp
new file mode 100644
index 0000000000..6a5d431bc8
--- /dev/null
+++ b/accessible/atk/nsMaiInterfaceText.cpp
@@ -0,0 +1,564 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InterfaceInitFuncs.h"
+#include "mozilla/a11y/PDocAccessible.h"
+#include "nsAccessibilityService.h"
+#include "LocalAccessible-inl.h"
+#include "HyperTextAccessible-inl.h"
+#include "nsMai.h"
+#include "RemoteAccessible.h"
+#include "AccAttributes.h"
+
+#include "nsIAccessibleTypes.h"
+#include "nsISimpleEnumerator.h"
+#include "nsUTF8Utils.h"
+
+#include "mozilla/Likely.h"
+
+#include "DOMtoATK.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+static const char* sAtkTextAttrNames[ATK_TEXT_ATTR_LAST_DEFINED];
+
+static AtkAttributeSet* ConvertToAtkTextAttributeSet(
+ AccAttributes* aAttributes) {
+ if (!aAttributes) {
+ // This can happen if an Accessible dies in the content process, but the
+ // parent hasn't been udpated yet.
+ return nullptr;
+ }
+
+ AtkAttributeSet* atkAttributeSet = nullptr;
+
+ for (auto iter : *aAttributes) {
+ AtkAttribute* atkAttr = (AtkAttribute*)g_malloc(sizeof(AtkAttribute));
+ nsAutoString value;
+ // We set atkAttr->name directly for each case. For the value, we set the
+ // value string for each case. atkAttr->value is set at the end based on the
+ // value string.
+
+ // Set atkAttr->name to a specific ATK attribute name.
+ auto atkName = [&atkAttr](AtkTextAttribute aAttrNum) {
+ atkAttr->name = g_strdup(sAtkTextAttrNames[aAttrNum]);
+ };
+ // Set value to a formatted ATK color value.
+ auto colorValue = [&iter, &value] {
+ // The format of the atk attribute is r,g,b and the gecko one is
+ // rgb(r, g, b).
+ auto color = iter.Value<Color>();
+ MOZ_ASSERT(color);
+ value.AppendInt(NS_GET_R(color->mValue));
+ value.Append(',');
+ value.AppendInt(NS_GET_G(color->mValue));
+ value.Append(',');
+ value.AppendInt(NS_GET_B(color->mValue));
+ };
+
+ nsAtom* name = iter.Name();
+ if (name == nsGkAtoms::color) {
+ atkName(ATK_TEXT_ATTR_FG_COLOR);
+ colorValue();
+ } else if (name == nsGkAtoms::backgroundColor) {
+ atkName(ATK_TEXT_ATTR_BG_COLOR);
+ colorValue();
+ } else if (name == nsGkAtoms::font_family) {
+ atkName(ATK_TEXT_ATTR_FAMILY_NAME);
+ iter.ValueAsString(value);
+ } else if (name == nsGkAtoms::font_size) {
+ atkName(ATK_TEXT_ATTR_SIZE);
+ // ATK wants the number of points without pt at the end.
+ auto fontSize = iter.Value<FontSize>();
+ MOZ_ASSERT(fontSize);
+ value.AppendInt(fontSize->mValue);
+ } else if (name == nsGkAtoms::fontWeight) {
+ atkName(ATK_TEXT_ATTR_WEIGHT);
+ iter.ValueAsString(value);
+ } else if (name == nsGkAtoms::invalid) {
+ atkName(ATK_TEXT_ATTR_INVALID);
+ iter.ValueAsString(value);
+ } else {
+ nsAutoString nameStr;
+ iter.NameAsString(nameStr);
+ atkAttr->name = g_strdup(NS_ConvertUTF16toUTF8(nameStr).get());
+ iter.ValueAsString(value);
+ }
+
+ atkAttr->value = g_strdup(NS_ConvertUTF16toUTF8(value).get());
+ atkAttributeSet = g_slist_prepend(atkAttributeSet, atkAttr);
+ }
+
+ // libatk-adaptor will free it
+ return atkAttributeSet;
+}
+
+extern "C" {
+
+static gchar* getTextCB(AtkText* aText, gint aStartOffset, gint aEndOffset) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
+ if (!acc || !acc->IsTextRole()) {
+ return nullptr;
+ }
+ HyperTextAccessibleBase* text = acc->AsHyperTextBase();
+ if (!text) {
+ return nullptr;
+ }
+ return DOMtoATK::NewATKString(text, aStartOffset, aEndOffset);
+}
+
+static gint getCharacterCountCB(AtkText* aText);
+
+// Note: this does not support magic offsets, which is fine for its callers
+// which do not implement any.
+static gchar* getCharTextAtOffset(AtkText* aText, gint aOffset,
+ gint* aStartOffset, gint* aEndOffset) {
+ gint end = aOffset + 1;
+ gint count = getCharacterCountCB(aText);
+
+ if (aOffset > count) {
+ aOffset = count;
+ }
+ if (end > count) {
+ end = count;
+ }
+ if (aOffset < 0) {
+ aOffset = 0;
+ }
+ if (end < 0) {
+ end = 0;
+ }
+ *aStartOffset = aOffset;
+ *aEndOffset = end;
+
+ return getTextCB(aText, aOffset, end);
+}
+
+static gchar* getTextAfterOffsetCB(AtkText* aText, gint aOffset,
+ AtkTextBoundary aBoundaryType,
+ gint* aStartOffset, gint* aEndOffset) {
+ if (aBoundaryType == ATK_TEXT_BOUNDARY_CHAR) {
+ return getCharTextAtOffset(aText, aOffset + 1, aStartOffset, aEndOffset);
+ }
+
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
+ if (!acc) {
+ return nullptr;
+ }
+
+ HyperTextAccessibleBase* text = acc->AsHyperTextBase();
+ if (!text || !acc->IsTextRole()) {
+ return nullptr;
+ }
+
+ nsAutoString autoStr;
+ int32_t startOffset = 0, endOffset = 0;
+ text->TextAfterOffset(aOffset, aBoundaryType, &startOffset, &endOffset,
+ autoStr);
+
+ *aStartOffset = startOffset;
+ *aEndOffset = endOffset;
+
+ // libspi will free it.
+ return DOMtoATK::Convert(autoStr);
+}
+
+static gchar* getTextAtOffsetCB(AtkText* aText, gint aOffset,
+ AtkTextBoundary aBoundaryType,
+ gint* aStartOffset, gint* aEndOffset) {
+ if (aBoundaryType == ATK_TEXT_BOUNDARY_CHAR) {
+ return getCharTextAtOffset(aText, aOffset, aStartOffset, aEndOffset);
+ }
+
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
+ if (!acc) {
+ return nullptr;
+ }
+
+ HyperTextAccessibleBase* text = acc->AsHyperTextBase();
+ if (!text || !acc->IsTextRole()) {
+ return nullptr;
+ }
+
+ nsAutoString autoStr;
+ int32_t startOffset = 0, endOffset = 0;
+ text->TextAtOffset(aOffset, aBoundaryType, &startOffset, &endOffset, autoStr);
+
+ *aStartOffset = startOffset;
+ *aEndOffset = endOffset;
+
+ // libspi will free it.
+ return DOMtoATK::Convert(autoStr);
+}
+
+static gunichar getCharacterAtOffsetCB(AtkText* aText, gint aOffset) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
+ if (!acc) {
+ return 0;
+ }
+
+ HyperTextAccessibleBase* text = acc->AsHyperTextBase();
+ if (text) {
+ return DOMtoATK::ATKCharacter(text, aOffset);
+ }
+
+ return 0;
+}
+
+static gchar* getTextBeforeOffsetCB(AtkText* aText, gint aOffset,
+ AtkTextBoundary aBoundaryType,
+ gint* aStartOffset, gint* aEndOffset) {
+ if (aBoundaryType == ATK_TEXT_BOUNDARY_CHAR) {
+ return getCharTextAtOffset(aText, aOffset - 1, aStartOffset, aEndOffset);
+ }
+
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
+ if (!acc) {
+ return nullptr;
+ }
+
+ HyperTextAccessibleBase* text = acc->AsHyperTextBase();
+ if (!text || !acc->IsTextRole()) {
+ return nullptr;
+ }
+
+ nsAutoString autoStr;
+ int32_t startOffset = 0, endOffset = 0;
+ text->TextBeforeOffset(aOffset, aBoundaryType, &startOffset, &endOffset,
+ autoStr);
+
+ *aStartOffset = startOffset;
+ *aEndOffset = endOffset;
+
+ // libspi will free it.
+ return DOMtoATK::Convert(autoStr);
+}
+
+static gint getCaretOffsetCB(AtkText* aText) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
+ if (!acc) {
+ return -1;
+ }
+
+ HyperTextAccessibleBase* text = acc->AsHyperTextBase();
+ if (!text || !acc->IsTextRole()) {
+ return -1;
+ }
+
+ return static_cast<gint>(text->CaretOffset());
+}
+
+static AtkAttributeSet* getRunAttributesCB(AtkText* aText, gint aOffset,
+ gint* aStartOffset,
+ gint* aEndOffset) {
+ *aStartOffset = -1;
+ *aEndOffset = -1;
+ int32_t startOffset = 0, endOffset = 0;
+
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
+ if (!acc) {
+ return nullptr;
+ }
+
+ HyperTextAccessibleBase* text = acc->AsHyperTextBase();
+ if (!text || !acc->IsTextRole()) {
+ return nullptr;
+ }
+
+ RefPtr<AccAttributes> attributes =
+ text->TextAttributes(false, aOffset, &startOffset, &endOffset);
+
+ *aStartOffset = startOffset;
+ *aEndOffset = endOffset;
+
+ return ConvertToAtkTextAttributeSet(attributes);
+}
+
+static AtkAttributeSet* getDefaultAttributesCB(AtkText* aText) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
+ if (!acc) {
+ return nullptr;
+ }
+
+ HyperTextAccessibleBase* text = acc->AsHyperTextBase();
+ if (!text || !acc->IsTextRole()) {
+ return nullptr;
+ }
+
+ RefPtr<AccAttributes> attributes = text->DefaultTextAttributes();
+ return ConvertToAtkTextAttributeSet(attributes);
+}
+
+static void getCharacterExtentsCB(AtkText* aText, gint aOffset, gint* aX,
+ gint* aY, gint* aWidth, gint* aHeight,
+ AtkCoordType aCoords) {
+ if (!aX || !aY || !aWidth || !aHeight) {
+ return;
+ }
+ *aX = *aY = *aWidth = *aHeight = -1;
+
+ uint32_t geckoCoordType;
+ if (aCoords == ATK_XY_SCREEN) {
+ geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE;
+ } else {
+ geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE;
+ }
+
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
+ if (!acc) {
+ return;
+ }
+
+ HyperTextAccessibleBase* text = acc->AsHyperTextBase();
+ if (!text || !acc->IsTextRole()) {
+ return;
+ }
+
+ LayoutDeviceIntRect rect = text->CharBounds(aOffset, geckoCoordType);
+
+ *aX = rect.x;
+ *aY = rect.y;
+ *aWidth = rect.width;
+ *aHeight = rect.height;
+}
+
+static void getRangeExtentsCB(AtkText* aText, gint aStartOffset,
+ gint aEndOffset, AtkCoordType aCoords,
+ AtkTextRectangle* aRect) {
+ if (!aRect) {
+ return;
+ }
+ aRect->x = aRect->y = aRect->width = aRect->height = -1;
+
+ uint32_t geckoCoordType;
+ if (aCoords == ATK_XY_SCREEN) {
+ geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE;
+ } else {
+ geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE;
+ }
+
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
+ if (!acc) {
+ return;
+ }
+
+ HyperTextAccessibleBase* text = acc->AsHyperTextBase();
+ if (!text || !acc->IsTextRole()) {
+ return;
+ }
+
+ LayoutDeviceIntRect rect =
+ text->TextBounds(aStartOffset, aEndOffset, geckoCoordType);
+
+ aRect->x = rect.x;
+ aRect->y = rect.y;
+ aRect->width = rect.width;
+ aRect->height = rect.height;
+}
+
+static gint getCharacterCountCB(AtkText* aText) {
+ if (Accessible* acc = GetInternalObj(ATK_OBJECT(aText))) {
+ if (HyperTextAccessibleBase* text = acc->AsHyperTextBase()) {
+ return static_cast<gint>(text->CharacterCount());
+ }
+ }
+ return 0;
+}
+
+static gint getOffsetAtPointCB(AtkText* aText, gint aX, gint aY,
+ AtkCoordType aCoords) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
+ if (!acc) {
+ return -1;
+ }
+ HyperTextAccessibleBase* text = acc->AsHyperTextBase();
+ if (!text || !acc->IsTextRole()) {
+ return -1;
+ }
+ return static_cast<gint>(text->OffsetAtPoint(
+ aX, aY,
+ (aCoords == ATK_XY_SCREEN
+ ? nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE
+ : nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE)));
+}
+
+static gint getTextSelectionCountCB(AtkText* aText) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
+ if (!acc) {
+ return 0;
+ }
+
+ HyperTextAccessibleBase* text = acc->AsHyperTextBase();
+ if (!text || !acc->IsTextRole()) {
+ return 0;
+ }
+
+ return text->SelectionCount();
+}
+
+static gchar* getTextSelectionCB(AtkText* aText, gint aSelectionNum,
+ gint* aStartOffset, gint* aEndOffset) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
+ if (!acc) {
+ return nullptr;
+ }
+
+ int32_t startOffset = 0, endOffset = 0;
+ HyperTextAccessibleBase* text = acc->AsHyperTextBase();
+ if (!text || !acc->IsTextRole()) {
+ return nullptr;
+ }
+
+ text->SelectionBoundsAt(aSelectionNum, &startOffset, &endOffset);
+ *aStartOffset = startOffset;
+ *aEndOffset = endOffset;
+
+ return getTextCB(aText, *aStartOffset, *aEndOffset);
+}
+
+// set methods
+static gboolean addTextSelectionCB(AtkText* aText, gint aStartOffset,
+ gint aEndOffset) {
+ AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
+ if (accWrap) {
+ HyperTextAccessible* text = accWrap->AsHyperText();
+ if (!text || !text->IsTextRole()) {
+ return FALSE;
+ }
+
+ return text->AddToSelection(aStartOffset, aEndOffset);
+ }
+ if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
+ return proxy->AddToSelection(aStartOffset, aEndOffset);
+ }
+
+ return FALSE;
+}
+
+static gboolean removeTextSelectionCB(AtkText* aText, gint aSelectionNum) {
+ AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
+ if (accWrap) {
+ HyperTextAccessible* text = accWrap->AsHyperText();
+ if (!text || !text->IsTextRole()) {
+ return FALSE;
+ }
+
+ return text->RemoveFromSelection(aSelectionNum);
+ }
+ if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
+ return proxy->RemoveFromSelection(aSelectionNum);
+ }
+
+ return FALSE;
+}
+
+static gboolean setTextSelectionCB(AtkText* aText, gint aSelectionNum,
+ gint aStartOffset, gint aEndOffset) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
+ if (!acc || !acc->IsTextRole()) {
+ return FALSE;
+ }
+ HyperTextAccessibleBase* text = acc->AsHyperTextBase();
+ if (!text) {
+ return FALSE;
+ }
+ return text->SetSelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset);
+}
+
+static gboolean setCaretOffsetCB(AtkText* aText, gint aOffset) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
+ if (!acc) {
+ return FALSE;
+ }
+
+ HyperTextAccessibleBase* text = acc->AsHyperTextBase();
+ if (!text || !acc->IsTextRole()) {
+ return FALSE;
+ }
+
+ text->SetCaretOffset(aOffset);
+ return TRUE;
+}
+
+static gboolean scrollSubstringToCB(AtkText* aText, gint aStartOffset,
+ gint aEndOffset, AtkScrollType aType) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
+ if (!acc) {
+ return FALSE;
+ }
+
+ HyperTextAccessibleBase* text = acc->AsHyperTextBase();
+ if (!text) {
+ return FALSE;
+ }
+
+ text->ScrollSubstringTo(aStartOffset, aEndOffset, aType);
+
+ return TRUE;
+}
+
+static gboolean scrollSubstringToPointCB(AtkText* aText, gint aStartOffset,
+ gint aEndOffset, AtkCoordType aCoords,
+ gint aX, gint aY) {
+ AtkObject* atkObject = ATK_OBJECT(aText);
+ AccessibleWrap* accWrap = GetAccessibleWrap(atkObject);
+ if (accWrap) {
+ HyperTextAccessible* text = accWrap->AsHyperText();
+ if (!text || !text->IsTextRole() ||
+ !text->IsValidRange(aStartOffset, aEndOffset)) {
+ return FALSE;
+ }
+ text->ScrollSubstringToPoint(aStartOffset, aEndOffset, aCoords, aX, aY);
+ return TRUE;
+ }
+
+ RemoteAccessible* proxy = GetProxy(atkObject);
+ if (proxy) {
+ proxy->ScrollSubstringToPoint(aStartOffset, aEndOffset, aCoords, aX, aY);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+}
+
+void textInterfaceInitCB(AtkTextIface* aIface) {
+ NS_ASSERTION(aIface, "Invalid aIface");
+ if (MOZ_UNLIKELY(!aIface)) return;
+
+ aIface->get_text = getTextCB;
+ aIface->get_text_after_offset = getTextAfterOffsetCB;
+ aIface->get_text_at_offset = getTextAtOffsetCB;
+ aIface->get_character_at_offset = getCharacterAtOffsetCB;
+ aIface->get_text_before_offset = getTextBeforeOffsetCB;
+ aIface->get_caret_offset = getCaretOffsetCB;
+ aIface->get_run_attributes = getRunAttributesCB;
+ aIface->get_default_attributes = getDefaultAttributesCB;
+ aIface->get_character_extents = getCharacterExtentsCB;
+ aIface->get_range_extents = getRangeExtentsCB;
+ aIface->get_character_count = getCharacterCountCB;
+ aIface->get_offset_at_point = getOffsetAtPointCB;
+ aIface->get_n_selections = getTextSelectionCountCB;
+ aIface->get_selection = getTextSelectionCB;
+
+ // set methods
+ aIface->add_selection = addTextSelectionCB;
+ aIface->remove_selection = removeTextSelectionCB;
+ aIface->set_selection = setTextSelectionCB;
+ aIface->set_caret_offset = setCaretOffsetCB;
+
+ if (IsAtkVersionAtLeast(2, 32)) {
+ aIface->scroll_substring_to = scrollSubstringToCB;
+ aIface->scroll_substring_to_point = scrollSubstringToPointCB;
+ }
+
+ // Cache the string values of the atk text attribute names.
+ for (uint32_t i = 0; i < ArrayLength(sAtkTextAttrNames); i++) {
+ sAtkTextAttrNames[i] =
+ atk_text_attribute_get_name(static_cast<AtkTextAttribute>(i));
+ }
+}
diff --git a/accessible/atk/nsMaiInterfaceValue.cpp b/accessible/atk/nsMaiInterfaceValue.cpp
new file mode 100644
index 0000000000..05a7da171e
--- /dev/null
+++ b/accessible/atk/nsMaiInterfaceValue.cpp
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InterfaceInitFuncs.h"
+
+#include "AccessibleWrap.h"
+#include "nsMai.h"
+#include "RemoteAccessible.h"
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/Likely.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+extern "C" {
+
+static void getCurrentValueCB(AtkValue* obj, GValue* value) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(obj));
+ if (!acc) {
+ return;
+ }
+
+ memset(value, 0, sizeof(GValue));
+ double accValue = acc->CurValue();
+ if (std::isnan(accValue)) return;
+
+ g_value_init(value, G_TYPE_DOUBLE);
+ g_value_set_double(value, accValue);
+}
+
+static void getMaximumValueCB(AtkValue* obj, GValue* value) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(obj));
+ if (!acc) {
+ return;
+ }
+
+ memset(value, 0, sizeof(GValue));
+ double accValue = acc->MaxValue();
+ if (std::isnan(accValue)) return;
+
+ g_value_init(value, G_TYPE_DOUBLE);
+ g_value_set_double(value, accValue);
+}
+
+static void getMinimumValueCB(AtkValue* obj, GValue* value) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(obj));
+ if (!acc) {
+ return;
+ }
+
+ memset(value, 0, sizeof(GValue));
+ double accValue = acc->MinValue();
+ if (std::isnan(accValue)) return;
+
+ g_value_init(value, G_TYPE_DOUBLE);
+ g_value_set_double(value, accValue);
+}
+
+static void getMinimumIncrementCB(AtkValue* obj, GValue* minimumIncrement) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(obj));
+ if (!acc) {
+ return;
+ }
+
+ memset(minimumIncrement, 0, sizeof(GValue));
+ double accValue = acc->Step();
+ if (std::isnan(accValue)) {
+ accValue = 0; // zero if the minimum increment is undefined
+ }
+
+ g_value_init(minimumIncrement, G_TYPE_DOUBLE);
+ g_value_set_double(minimumIncrement, accValue);
+}
+
+static gboolean setCurrentValueCB(AtkValue* obj, const GValue* value) {
+ Accessible* acc = GetInternalObj(ATK_OBJECT(obj));
+ if (!acc) {
+ return false;
+ }
+
+ double accValue = g_value_get_double(value);
+ return acc->SetCurValue(accValue);
+}
+
+void valueInterfaceInitCB(AtkValueIface* aIface) {
+ NS_ASSERTION(aIface, "Invalid aIface");
+ if (MOZ_UNLIKELY(!aIface)) return;
+
+ aIface->get_current_value = getCurrentValueCB;
+ aIface->get_maximum_value = getMaximumValueCB;
+ aIface->get_minimum_value = getMinimumValueCB;
+ aIface->get_minimum_increment = getMinimumIncrementCB;
+ aIface->set_current_value = setCurrentValueCB;
+}
+}
diff --git a/accessible/atk/nsStateMap.h b/accessible/atk/nsStateMap.h
new file mode 100644
index 0000000000..3587ccd6cf
--- /dev/null
+++ b/accessible/atk/nsStateMap.h
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <atk/atk.h>
+#include "AccessibleWrap.h"
+
+#include <type_traits>
+
+// clang-format off
+/******************************************************************************
+The following accessible states aren't translated, just ignored:
+ STATE_READONLY: Supported indirectly via EXT_STATE_EDITABLE
+ STATE_HOTTRACKED: No ATK equivalent. No known use case.
+ The nsIAccessible state is not currently supported.
+ STATE_FLOATING: No ATK equivalent. No known use case.
+ The nsIAccessible state is not currently supported.
+ STATE_MOVEABLE: No ATK equivalent. No known use case.
+ The nsIAccessible state is not currently supported.
+ STATE_SELFVOICING: No ATK equivalent -- the object has self-TTS.
+ The nsIAccessible state is not currently supported.
+ STATE_LINKED: The object is formatted as a hyperlink. Supported via ATK_ROLE_LINK.
+ STATE_EXTSELECTABLE: Indicates that an object extends its selection.
+ This is supported via STATE_MULTISELECTABLE.
+ STATE_PROTECTED: The object is a password-protected edit control.
+ Supported via ATK_ROLE_PASSWORD_TEXT
+ STATE_PINNED: The object is pinned, usually indicating it is fixed in
+ place and has permanence. No ATK equivalent. The
+ accessible state is not currently supported.
+
+The following ATK states are not supported:
+ ATK_STATE_ARMED: No clear use case, used briefly when button is activated
+ ATK_STATE_HAS_TOOLTIP: No clear use case, no IA2 equivalent
+ ATK_STATE_ICONIFIED: Mozilla does not have elements which are collapsable into icons
+ ATK_STATE_TRUNCATED: No clear use case. Indicates that an object's onscreen content is truncated,
+ e.g. a text value in a spreadsheet cell. No IA2 state.
+******************************************************************************/
+// clang-format on
+
+enum EStateMapEntryType {
+ kMapDirectly,
+ kMapOpposite, // For example, UNAVAILABLE is the opposite of ENABLED
+ kNoStateChange, // Don't fire state change event
+};
+
+const AtkStateType kNone = ATK_STATE_INVALID;
+
+struct AtkStateMap {
+ AtkStateType atkState;
+ EStateMapEntryType stateMapEntryType;
+};
+
+// Map array from cross platform states to ATK states
+static const AtkStateMap gAtkStateMap[] =
+ {
+ // Cross Platform States
+ // clang-format off
+ { kNone, kMapOpposite }, // states::UNAVAILABLE = 1 << 0
+ { ATK_STATE_SELECTED, kMapDirectly }, // states::SELECTED = 1 << 1
+ { ATK_STATE_FOCUSED, kMapDirectly }, // states::FOCUSED = 1 << 2
+ { ATK_STATE_PRESSED, kMapDirectly }, // states::PRESSED = 1 << 3
+ { ATK_STATE_CHECKED, kMapDirectly }, // states::CHECKED = 1 << 4
+ { ATK_STATE_INDETERMINATE, kMapDirectly }, // states::MIXED = 1 << 5
+ { kNone, kMapDirectly }, // states::READONLY = 1 << 6
+ { kNone, kMapDirectly }, // states::HOTTRACKED = 1 << 7
+ { ATK_STATE_DEFAULT, kMapDirectly }, // states::DEFAULT = 1 << 8
+ { ATK_STATE_EXPANDED, kMapDirectly }, // states::EXPANDED = 1 << 9
+ { kNone, kNoStateChange }, // states::COLLAPSED = 1 << 10
+ { ATK_STATE_BUSY, kMapDirectly }, // states::BUSY = 1 << 11
+ { kNone, kMapDirectly }, // states::FLOATING = 1 << 12
+ { ATK_STATE_CHECKABLE, kMapDirectly }, // states::CHECKABLE = 1 << 13
+ { ATK_STATE_ANIMATED, kMapDirectly }, // states::ANIMATED = 1 << 14
+ { ATK_STATE_VISIBLE, kMapOpposite }, // states::INVISIBLE = 1 << 15
+ { ATK_STATE_SHOWING, kMapOpposite }, // states::OFFSCREEN = 1 << 16
+ { ATK_STATE_RESIZABLE, kMapDirectly }, // states::SIZEABLE = 1 << 17
+ { kNone, kMapDirectly }, // states::MOVEABLE = 1 << 18
+ { kNone, kMapDirectly }, // states::SELFVOICING = 1 << 19
+ { ATK_STATE_FOCUSABLE, kMapDirectly }, // states::FOCUSABLE = 1 << 20
+ { ATK_STATE_SELECTABLE, kMapDirectly }, // states::SELECTABLE = 1 << 21
+ { kNone, kMapDirectly }, // states::LINKED = 1 << 22
+ { ATK_STATE_VISITED, kMapDirectly }, // states::TRAVERSED = 1 << 23
+ { ATK_STATE_MULTISELECTABLE, kMapDirectly }, // states::MULTISELECTABLE = 1 << 24
+ { kNone, kMapDirectly }, // states::EXTSELECTABLE = 1 << 25
+ { ATK_STATE_REQUIRED, kMapDirectly }, // states::STATE_REQUIRED = 1 << 26
+ { kNone, kMapDirectly }, // states::ALERT_MEDIUM = 1 << 27
+ { ATK_STATE_INVALID_ENTRY, kMapDirectly }, // states::INVALID = 1 << 28
+ { kNone, kMapDirectly }, // states::PROTECTED = 1 << 29
+ { ATK_STATE_HAS_POPUP, kMapDirectly }, // states::HASPOPUP = 1 << 30
+ { ATK_STATE_SUPPORTS_AUTOCOMPLETION, kMapDirectly }, // states::SUPPORTS_AUTOCOMPLETION = 1 << 31
+ { ATK_STATE_DEFUNCT, kMapDirectly }, // states::DEFUNCT = 1 << 32
+ { ATK_STATE_SELECTABLE_TEXT, kMapDirectly }, // states::SELECTABLE_TEXT = 1 << 33
+ { ATK_STATE_EDITABLE, kMapDirectly }, // states::EDITABLE = 1 << 34
+ { ATK_STATE_ACTIVE, kMapDirectly }, // states::ACTIVE = 1 << 35
+ { ATK_STATE_MODAL, kMapDirectly }, // states::MODAL = 1 << 36
+ { ATK_STATE_MULTI_LINE, kMapDirectly }, // states::MULTI_LINE = 1 << 37
+ { ATK_STATE_HORIZONTAL, kMapDirectly }, // states::HORIZONTAL = 1 << 38
+ { ATK_STATE_OPAQUE, kMapDirectly }, // states::OPAQUE = 1 << 39
+ { ATK_STATE_SINGLE_LINE, kMapDirectly }, // states::SINGLE_LINE = 1 << 40
+ { ATK_STATE_TRANSIENT, kMapDirectly }, // states::TRANSIENT = 1 << 41
+ { ATK_STATE_VERTICAL, kMapDirectly }, // states::VERTICAL = 1 << 42
+ { ATK_STATE_STALE, kMapDirectly }, // states::STALE = 1 << 43
+ { ATK_STATE_ENABLED, kMapDirectly }, // states::ENABLED = 1 << 44
+ { ATK_STATE_SENSITIVE, kMapDirectly }, // states::SENSITIVE = 1 << 45
+ { ATK_STATE_EXPANDABLE, kMapDirectly }, // states::EXPANDABLE = 1 << 46
+ { kNone, kMapDirectly }, // states::PINNED = 1 << 47
+ { ATK_STATE_ACTIVE, kMapDirectly } // states::CURRENT = 1 << 48
+ // clang-format on
+};
+
+static const auto gAtkStateMapLen = std::extent<decltype(gAtkStateMap)>::value;
+
+static_assert(((uint64_t)0x1) << (gAtkStateMapLen - 1) ==
+ mozilla::a11y::states::LAST_ENTRY,
+ "ATK states map is out of sync with internal states");