tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

AccessibleWrap.cpp (41249B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "AccessibleWrap.h"
      8 
      9 #include "LocalAccessible-inl.h"
     10 #include "AccAttributes.h"
     11 #include "ApplicationAccessibleWrap.h"
     12 #include "InterfaceInitFuncs.h"
     13 #include "nsAccUtils.h"
     14 #include "mozilla/a11y/PDocAccessible.h"
     15 #include "OuterDocAccessible.h"
     16 #include "RemoteAccessible.h"
     17 #include "DocAccessibleParent.h"
     18 #include "RootAccessible.h"
     19 #include "mozilla/a11y/TableAccessible.h"
     20 #include "mozilla/a11y/TableCellAccessible.h"
     21 #include "nsMai.h"
     22 #include "nsMaiHyperlink.h"
     23 #include "nsString.h"
     24 #include "nsStateMap.h"
     25 #include "mozilla/a11y/Platform.h"
     26 #include "Relation.h"
     27 #include "RootAccessible.h"
     28 #include "States.h"
     29 #include "nsISimpleEnumerator.h"
     30 
     31 #include "mozilla/Sprintf.h"
     32 #include "nsAccessibilityService.h"
     33 #include "nsComponentManagerUtils.h"
     34 
     35 using namespace mozilla;
     36 using namespace mozilla::a11y;
     37 
     38 MaiAtkObject::EAvailableAtkSignals MaiAtkObject::gAvailableAtkSignals =
     39    eUnknown;
     40 
     41 // defined in ApplicationAccessibleWrap.cpp
     42 extern "C" GType g_atk_hyperlink_impl_type;
     43 
     44 /* MaiAtkObject */
     45 
     46 enum {
     47  ACTIVATE,
     48  CREATE,
     49  DEACTIVATE,
     50  DESTROY,
     51  MAXIMIZE,
     52  MINIMIZE,
     53  RESIZE,
     54  RESTORE,
     55  LAST_SIGNAL
     56 };
     57 
     58 enum MaiInterfaceType {
     59  MAI_INTERFACE_COMPONENT, /* 0 */
     60  MAI_INTERFACE_ACTION,
     61  MAI_INTERFACE_VALUE,
     62  MAI_INTERFACE_EDITABLE_TEXT,
     63  MAI_INTERFACE_HYPERTEXT,
     64  MAI_INTERFACE_HYPERLINK_IMPL,
     65  MAI_INTERFACE_SELECTION,
     66  MAI_INTERFACE_TABLE,
     67  MAI_INTERFACE_TEXT,
     68  MAI_INTERFACE_DOCUMENT,
     69  MAI_INTERFACE_IMAGE, /* 10 */
     70  MAI_INTERFACE_TABLE_CELL
     71 };
     72 
     73 static GType GetAtkTypeForMai(MaiInterfaceType type) {
     74  switch (type) {
     75    case MAI_INTERFACE_COMPONENT:
     76      return ATK_TYPE_COMPONENT;
     77    case MAI_INTERFACE_ACTION:
     78      return ATK_TYPE_ACTION;
     79    case MAI_INTERFACE_VALUE:
     80      return ATK_TYPE_VALUE;
     81    case MAI_INTERFACE_EDITABLE_TEXT:
     82      return ATK_TYPE_EDITABLE_TEXT;
     83    case MAI_INTERFACE_HYPERTEXT:
     84      return ATK_TYPE_HYPERTEXT;
     85    case MAI_INTERFACE_HYPERLINK_IMPL:
     86      return g_atk_hyperlink_impl_type;
     87    case MAI_INTERFACE_SELECTION:
     88      return ATK_TYPE_SELECTION;
     89    case MAI_INTERFACE_TABLE:
     90      return ATK_TYPE_TABLE;
     91    case MAI_INTERFACE_TEXT:
     92      return ATK_TYPE_TEXT;
     93    case MAI_INTERFACE_DOCUMENT:
     94      return ATK_TYPE_DOCUMENT;
     95    case MAI_INTERFACE_IMAGE:
     96      return ATK_TYPE_IMAGE;
     97    case MAI_INTERFACE_TABLE_CELL:
     98      MOZ_ASSERT(false);
     99  }
    100  return G_TYPE_INVALID;
    101 }
    102 
    103 #define NON_USER_EVENT ":system"
    104 
    105 // The atk interfaces we can expose without checking what version of ATK we are
    106 // dealing with.  At the moment AtkTableCell is the only interface we can't
    107 // always expose.
    108 static const GInterfaceInfo atk_if_infos[] = {
    109    {(GInterfaceInitFunc)componentInterfaceInitCB,
    110     (GInterfaceFinalizeFunc) nullptr, nullptr},
    111    {(GInterfaceInitFunc)actionInterfaceInitCB,
    112     (GInterfaceFinalizeFunc) nullptr, nullptr},
    113    {(GInterfaceInitFunc)valueInterfaceInitCB, (GInterfaceFinalizeFunc) nullptr,
    114     nullptr},
    115    {(GInterfaceInitFunc)editableTextInterfaceInitCB,
    116     (GInterfaceFinalizeFunc) nullptr, nullptr},
    117    {(GInterfaceInitFunc)hypertextInterfaceInitCB,
    118     (GInterfaceFinalizeFunc) nullptr, nullptr},
    119    {(GInterfaceInitFunc)hyperlinkImplInterfaceInitCB,
    120     (GInterfaceFinalizeFunc) nullptr, nullptr},
    121    {(GInterfaceInitFunc)selectionInterfaceInitCB,
    122     (GInterfaceFinalizeFunc) nullptr, nullptr},
    123    {(GInterfaceInitFunc)tableInterfaceInitCB, (GInterfaceFinalizeFunc) nullptr,
    124     nullptr},
    125    {(GInterfaceInitFunc)textInterfaceInitCB, (GInterfaceFinalizeFunc) nullptr,
    126     nullptr},
    127    {(GInterfaceInitFunc)documentInterfaceInitCB,
    128     (GInterfaceFinalizeFunc) nullptr, nullptr},
    129    {(GInterfaceInitFunc)imageInterfaceInitCB, (GInterfaceFinalizeFunc) nullptr,
    130     nullptr}};
    131 
    132 static GQuark quark_mai_hyperlink = 0;
    133 
    134 AtkHyperlink* MaiAtkObject::GetAtkHyperlink() {
    135  NS_ASSERTION(quark_mai_hyperlink, "quark_mai_hyperlink not initialized");
    136  MaiHyperlink* maiHyperlink =
    137      (MaiHyperlink*)g_object_get_qdata(G_OBJECT(this), quark_mai_hyperlink);
    138  if (!maiHyperlink) {
    139    maiHyperlink = new MaiHyperlink(acc);
    140    g_object_set_qdata(G_OBJECT(this), quark_mai_hyperlink, maiHyperlink);
    141  }
    142 
    143  return maiHyperlink->GetAtkHyperlink();
    144 }
    145 
    146 void MaiAtkObject::Shutdown() {
    147  acc = nullptr;
    148  MaiHyperlink* maiHyperlink =
    149      (MaiHyperlink*)g_object_get_qdata(G_OBJECT(this), quark_mai_hyperlink);
    150  if (maiHyperlink) {
    151    delete maiHyperlink;
    152    g_object_set_qdata(G_OBJECT(this), quark_mai_hyperlink, nullptr);
    153  }
    154 }
    155 
    156 struct MaiAtkObjectClass {
    157  AtkObjectClass parent_class;
    158 };
    159 
    160 static guint mai_atk_object_signals[LAST_SIGNAL] = {
    161    0,
    162 };
    163 
    164 static void MaybeFireNameChange(AtkObject* aAtkObj, const nsString& aNewName);
    165 
    166 G_BEGIN_DECLS
    167 /* callbacks for MaiAtkObject */
    168 static void classInitCB(AtkObjectClass* aClass);
    169 static void initializeCB(AtkObject* aAtkObj, gpointer aData);
    170 static void finalizeCB(GObject* aObj);
    171 
    172 /* callbacks for AtkObject virtual functions */
    173 static const gchar* getNameCB(AtkObject* aAtkObj);
    174 /* getDescriptionCB is also used by image interface */
    175 const gchar* getDescriptionCB(AtkObject* aAtkObj);
    176 static AtkRole getRoleCB(AtkObject* aAtkObj);
    177 static AtkAttributeSet* getAttributesCB(AtkObject* aAtkObj);
    178 static const gchar* GetLocaleCB(AtkObject*);
    179 static AtkObject* getParentCB(AtkObject* aAtkObj);
    180 static gint getChildCountCB(AtkObject* aAtkObj);
    181 static AtkObject* refChildCB(AtkObject* aAtkObj, gint aChildIndex);
    182 static gint getIndexInParentCB(AtkObject* aAtkObj);
    183 static AtkStateSet* refStateSetCB(AtkObject* aAtkObj);
    184 static AtkRelationSet* refRelationSetCB(AtkObject* aAtkObj);
    185 
    186 /* the missing atkobject virtual functions */
    187 /*
    188  static AtkLayer            getLayerCB(AtkObject *aAtkObj);
    189  static gint                getMdiZorderCB(AtkObject *aAtkObj);
    190  static void                SetNameCB(AtkObject *aAtkObj,
    191  const gchar *name);
    192  static void                SetDescriptionCB(AtkObject *aAtkObj,
    193  const gchar *description);
    194  static void                SetParentCB(AtkObject *aAtkObj,
    195  AtkObject *parent);
    196  static void                SetRoleCB(AtkObject *aAtkObj,
    197  AtkRole role);
    198  static guint               ConnectPropertyChangeHandlerCB(
    199  AtkObject  *aObj,
    200  AtkPropertyChangeHandler *handler);
    201  static void                RemovePropertyChangeHandlerCB(
    202  AtkObject *aAtkObj,
    203  guint handler_id);
    204  static void                InitializeCB(AtkObject *aAtkObj,
    205  gpointer data);
    206  static void                ChildrenChangedCB(AtkObject *aAtkObj,
    207  guint change_index,
    208  gpointer changed_child);
    209  static void                FocusEventCB(AtkObject *aAtkObj,
    210  gboolean focus_in);
    211  static void                PropertyChangeCB(AtkObject *aAtkObj,
    212  AtkPropertyValues *values);
    213  static void                StateChangeCB(AtkObject *aAtkObj,
    214  const gchar *name,
    215  gboolean state_set);
    216  static void                VisibleDataChangedCB(AtkObject *aAtkObj);
    217 */
    218 G_END_DECLS
    219 
    220 static GType GetMaiAtkType(uint16_t interfacesBits);
    221 static const char* GetUniqueMaiAtkTypeName(uint16_t interfacesBits);
    222 
    223 static gpointer parent_class = nullptr;
    224 
    225 GType mai_atk_object_get_type(void) {
    226  static GType type = 0;
    227 
    228  if (!type) {
    229    static const GTypeInfo tinfo = {
    230        sizeof(MaiAtkObjectClass),
    231        (GBaseInitFunc) nullptr,
    232        (GBaseFinalizeFunc) nullptr,
    233        (GClassInitFunc)classInitCB,
    234        (GClassFinalizeFunc) nullptr,
    235        nullptr,              /* class data */
    236        sizeof(MaiAtkObject), /* instance size */
    237        0,                    /* nb preallocs */
    238        (GInstanceInitFunc) nullptr,
    239        nullptr /* value table */
    240    };
    241 
    242    type = g_type_register_static(ATK_TYPE_OBJECT, "MaiAtkObject", &tinfo,
    243                                  GTypeFlags(0));
    244    quark_mai_hyperlink = g_quark_from_static_string("MaiHyperlink");
    245  }
    246  return type;
    247 }
    248 
    249 AccessibleWrap::AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc)
    250    : LocalAccessible(aContent, aDoc), mAtkObject(nullptr) {}
    251 
    252 AccessibleWrap::~AccessibleWrap() {
    253  NS_ASSERTION(!mAtkObject, "ShutdownAtkObject() is not called");
    254 }
    255 
    256 void AccessibleWrap::ShutdownAtkObject() {
    257  if (!mAtkObject) return;
    258 
    259  NS_ASSERTION(IS_MAI_OBJECT(mAtkObject), "wrong type of atk object");
    260  if (IS_MAI_OBJECT(mAtkObject)) MAI_ATK_OBJECT(mAtkObject)->Shutdown();
    261 
    262  g_object_unref(mAtkObject);
    263  mAtkObject = nullptr;
    264 }
    265 
    266 void AccessibleWrap::Shutdown() {
    267  ShutdownAtkObject();
    268  LocalAccessible::Shutdown();
    269 }
    270 
    271 static uint16_t CreateMaiInterfaces(Accessible* aAccessible) {
    272  uint16_t interfaces = 1 << MAI_INTERFACE_COMPONENT;
    273 
    274  if (aAccessible->IsHyperText() && aAccessible->IsTextRole()) {
    275    interfaces |= (1 << MAI_INTERFACE_HYPERTEXT) | (1 << MAI_INTERFACE_TEXT) |
    276                  (1 << MAI_INTERFACE_EDITABLE_TEXT);
    277  }
    278 
    279  if (aAccessible->IsLink()) {
    280    interfaces |= 1 << MAI_INTERFACE_HYPERLINK_IMPL;
    281  }
    282 
    283  if (aAccessible->HasNumericValue()) {
    284    interfaces |= 1 << MAI_INTERFACE_VALUE;
    285  }
    286 
    287  if (aAccessible->IsTable()) {
    288    interfaces |= 1 << MAI_INTERFACE_TABLE;
    289  }
    290 
    291  if (aAccessible->IsTableCell()) {
    292    interfaces |= 1 << MAI_INTERFACE_TABLE_CELL;
    293  }
    294 
    295  if (aAccessible->IsImage()) {
    296    interfaces |= 1 << MAI_INTERFACE_IMAGE;
    297  }
    298 
    299  if (aAccessible->IsDoc()) {
    300    interfaces |= 1 << MAI_INTERFACE_DOCUMENT;
    301  }
    302 
    303  if (aAccessible->IsSelect()) {
    304    interfaces |= 1 << MAI_INTERFACE_SELECTION;
    305  }
    306 
    307  // XXX: Always include the action interface because aria-actions
    308  // can define actions mid-life.
    309  interfaces |= 1 << MAI_INTERFACE_ACTION;
    310 
    311  return interfaces;
    312 }
    313 
    314 void AccessibleWrap::GetNativeInterface(void** aOutAccessible) {
    315  *aOutAccessible = nullptr;
    316 
    317  if (!mAtkObject) {
    318    if (IsDefunct() || IsText()) {
    319      // We don't create ATK objects for node which has been shutdown or
    320      // plain text leaves
    321      return;
    322    }
    323 
    324    GType type = GetMaiAtkType(CreateMaiInterfaces(this));
    325    if (!type) return;
    326 
    327    mAtkObject = reinterpret_cast<AtkObject*>(g_object_new(type, nullptr));
    328    if (!mAtkObject) return;
    329 
    330    atk_object_initialize(mAtkObject, static_cast<Accessible*>(this));
    331    mAtkObject->role = ATK_ROLE_INVALID;
    332    mAtkObject->layer = ATK_LAYER_INVALID;
    333  }
    334 
    335  *aOutAccessible = mAtkObject;
    336 }
    337 
    338 AtkObject* AccessibleWrap::GetAtkObject(void) {
    339  void* atkObj = nullptr;
    340  GetNativeInterface(&atkObj);
    341  return static_cast<AtkObject*>(atkObj);
    342 }
    343 
    344 // Get AtkObject from LocalAccessible interface
    345 /* static */
    346 AtkObject* AccessibleWrap::GetAtkObject(LocalAccessible* acc) {
    347  void* atkObjPtr = nullptr;
    348  acc->GetNativeInterface(&atkObjPtr);
    349  return atkObjPtr ? ATK_OBJECT(atkObjPtr) : nullptr;
    350 }
    351 
    352 static GType GetMaiAtkType(uint16_t interfacesBits) {
    353  GType type;
    354  static const GTypeInfo tinfo = {
    355      sizeof(MaiAtkObjectClass),
    356      (GBaseInitFunc) nullptr,
    357      (GBaseFinalizeFunc) nullptr,
    358      (GClassInitFunc) nullptr,
    359      (GClassFinalizeFunc) nullptr,
    360      nullptr,              /* class data */
    361      sizeof(MaiAtkObject), /* instance size */
    362      0,                    /* nb preallocs */
    363      (GInstanceInitFunc) nullptr,
    364      nullptr /* value table */
    365  };
    366 
    367  /*
    368   * The members we use to register GTypes are GetAtkTypeForMai
    369   * and atk_if_infos, which are constant values to each MaiInterface
    370   * So we can reuse the registered GType when having
    371   * the same MaiInterface types.
    372   */
    373  const char* atkTypeName = GetUniqueMaiAtkTypeName(interfacesBits);
    374  type = g_type_from_name(atkTypeName);
    375  if (type) {
    376    return type;
    377  }
    378 
    379  /*
    380   * gobject limits the number of types that can directly derive from any
    381   * given object type to 4095.
    382   */
    383  static uint16_t typeRegCount = 0;
    384  if (typeRegCount++ >= 4095) {
    385    return G_TYPE_INVALID;
    386  }
    387  type = g_type_register_static(MAI_TYPE_ATK_OBJECT, atkTypeName, &tinfo,
    388                                GTypeFlags(0));
    389 
    390  for (uint32_t index = 0; index < std::size(atk_if_infos); index++) {
    391    if (interfacesBits & (1 << index)) {
    392      g_type_add_interface_static(type,
    393                                  GetAtkTypeForMai((MaiInterfaceType)index),
    394                                  &atk_if_infos[index]);
    395    }
    396  }
    397 
    398  // Special case AtkTableCell so we can check what version of Atk we are
    399  // dealing with.
    400  if (IsAtkVersionAtLeast(2, 12) &&
    401      (interfacesBits & (1 << MAI_INTERFACE_TABLE_CELL))) {
    402    const GInterfaceInfo cellInfo = {
    403        (GInterfaceInitFunc)tableCellInterfaceInitCB,
    404        (GInterfaceFinalizeFunc) nullptr, nullptr};
    405    g_type_add_interface_static(type, gAtkTableCellGetTypeFunc(), &cellInfo);
    406  }
    407 
    408  return type;
    409 }
    410 
    411 static const char* GetUniqueMaiAtkTypeName(uint16_t interfacesBits) {
    412 #define MAI_ATK_TYPE_NAME_LEN (30) /* 10+sizeof(uint16_t)*8/4+1 < 30 */
    413 
    414  static gchar namePrefix[] = "MaiAtkType"; /* size = 10 */
    415  static gchar name[MAI_ATK_TYPE_NAME_LEN + 1];
    416 
    417  SprintfLiteral(name, "%s%x", namePrefix, interfacesBits);
    418  name[MAI_ATK_TYPE_NAME_LEN] = '\0';
    419 
    420  return name;
    421 }
    422 
    423 bool AccessibleWrap::IsValidObject() {
    424  // to ensure we are not shut down
    425  return !IsDefunct();
    426 }
    427 
    428 /* static functions for ATK callbacks */
    429 void classInitCB(AtkObjectClass* aClass) {
    430  GObjectClass* gobject_class = G_OBJECT_CLASS(aClass);
    431 
    432  parent_class = g_type_class_peek_parent(aClass);
    433 
    434  aClass->get_name = getNameCB;
    435  aClass->get_description = getDescriptionCB;
    436  aClass->get_parent = getParentCB;
    437  aClass->get_n_children = getChildCountCB;
    438  aClass->ref_child = refChildCB;
    439  aClass->get_index_in_parent = getIndexInParentCB;
    440  aClass->get_role = getRoleCB;
    441  aClass->get_attributes = getAttributesCB;
    442  aClass->get_object_locale = GetLocaleCB;
    443  aClass->ref_state_set = refStateSetCB;
    444  aClass->ref_relation_set = refRelationSetCB;
    445 
    446  aClass->initialize = initializeCB;
    447 
    448  gobject_class->finalize = finalizeCB;
    449 
    450  mai_atk_object_signals[ACTIVATE] = g_signal_new(
    451      "activate", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST,
    452      0, /* default signal handler */
    453      nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
    454  mai_atk_object_signals[CREATE] = g_signal_new(
    455      "create", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST,
    456      0, /* default signal handler */
    457      nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
    458  mai_atk_object_signals[DEACTIVATE] = g_signal_new(
    459      "deactivate", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST,
    460      0, /* default signal handler */
    461      nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
    462  mai_atk_object_signals[DESTROY] = g_signal_new(
    463      "destroy", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST,
    464      0, /* default signal handler */
    465      nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
    466  mai_atk_object_signals[MAXIMIZE] = g_signal_new(
    467      "maximize", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST,
    468      0, /* default signal handler */
    469      nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
    470  mai_atk_object_signals[MINIMIZE] = g_signal_new(
    471      "minimize", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST,
    472      0, /* default signal handler */
    473      nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
    474  mai_atk_object_signals[RESIZE] = g_signal_new(
    475      "resize", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST,
    476      0, /* default signal handler */
    477      nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
    478  mai_atk_object_signals[RESTORE] = g_signal_new(
    479      "restore", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST,
    480      0, /* default signal handler */
    481      nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
    482 }
    483 
    484 void initializeCB(AtkObject* aAtkObj, gpointer aData) {
    485  NS_ASSERTION((IS_MAI_OBJECT(aAtkObj)), "Invalid AtkObject");
    486  NS_ASSERTION(aData, "Invalid Data to init AtkObject");
    487  if (!aAtkObj || !aData) return;
    488 
    489  /* call parent init function */
    490  /* AtkObjectClass has not a "initialize" function now,
    491   * maybe it has later
    492   */
    493 
    494  if (ATK_OBJECT_CLASS(parent_class)->initialize) {
    495    ATK_OBJECT_CLASS(parent_class)->initialize(aAtkObj, aData);
    496  }
    497 
    498  /* initialize object */
    499  MAI_ATK_OBJECT(aAtkObj)->acc = static_cast<Accessible*>(aData);
    500 }
    501 
    502 void finalizeCB(GObject* aObj) {
    503  if (!IS_MAI_OBJECT(aObj)) return;
    504  NS_ASSERTION(!MAI_ATK_OBJECT(aObj)->acc, "acc NOT null");
    505 
    506  // call parent finalize function
    507  // finalize of GObjectClass will unref the accessible parent if has
    508  if (G_OBJECT_CLASS(parent_class)->finalize) {
    509    G_OBJECT_CLASS(parent_class)->finalize(aObj);
    510  }
    511 }
    512 
    513 const gchar* getNameCB(AtkObject* aAtkObj) {
    514  nsAutoString name;
    515  if (Accessible* acc = GetInternalObj(aAtkObj)) {
    516    acc->Name(name);
    517  } else {
    518    return nullptr;
    519  }
    520 
    521  // XXX Firing an event from here does not seem right
    522  MaybeFireNameChange(aAtkObj, name);
    523 
    524  return aAtkObj->name;
    525 }
    526 
    527 static void MaybeFireNameChange(AtkObject* aAtkObj, const nsString& aNewName) {
    528  NS_ConvertUTF16toUTF8 newNameUTF8(aNewName);
    529  if (aAtkObj->name && !strcmp(aAtkObj->name, newNameUTF8.get())) return;
    530 
    531  // Below we duplicate the functionality of atk_object_set_name(),
    532  // but without calling atk_object_get_name(). Instead of
    533  // atk_object_get_name() we directly access aAtkObj->name. This is because
    534  // atk_object_get_name() would call getNameCB() which would call
    535  // MaybeFireNameChange() (or atk_object_set_name() before this problem was
    536  // fixed) and we would get an infinite recursion.
    537  // See http://bugzilla.mozilla.org/733712
    538 
    539  // Do not notify for initial name setting.
    540  // See bug http://bugzilla.gnome.org/665870
    541  bool notify = !!aAtkObj->name;
    542 
    543  free(aAtkObj->name);
    544  aAtkObj->name = strdup(newNameUTF8.get());
    545 
    546  if (notify) g_object_notify(G_OBJECT(aAtkObj), "accessible-name");
    547 }
    548 
    549 const gchar* getDescriptionCB(AtkObject* aAtkObj) {
    550  nsAutoString uniDesc;
    551  if (Accessible* acc = GetInternalObj(aAtkObj)) {
    552    acc->Description(uniDesc);
    553  } else {
    554    return nullptr;
    555  }
    556 
    557  NS_ConvertUTF8toUTF16 objDesc(aAtkObj->description);
    558  if (!uniDesc.Equals(objDesc)) {
    559    atk_object_set_description(aAtkObj, NS_ConvertUTF16toUTF8(uniDesc).get());
    560  }
    561 
    562  return aAtkObj->description;
    563 }
    564 
    565 AtkRole getRoleCB(AtkObject* aAtkObj) {
    566  if (aAtkObj->role != ATK_ROLE_INVALID) return aAtkObj->role;
    567 
    568  Accessible* acc = GetInternalObj(aAtkObj);
    569  if (!acc) {
    570    return ATK_ROLE_INVALID;
    571  }
    572 
    573 #ifdef DEBUG
    574  if (AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj)) {
    575    NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(accWrap),
    576                 "Does not support Text interface when it should");
    577  }
    578 #endif
    579 
    580 #define ROLE(geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \
    581             msaaRole, ia2Role, androidClass, iosIsElement, uiaControlType, \
    582             nameRule)                                                      \
    583  case roles::geckoRole:                                                    \
    584    aAtkObj->role = atkRole;                                                \
    585    break;
    586 
    587  switch (acc->Role()) {
    588 #include "RoleMap.h"
    589    default:
    590      MOZ_CRASH("Unknown role.");
    591  }
    592 
    593 #undef ROLE
    594 
    595  if (aAtkObj->role == ATK_ROLE_LIST_BOX && !IsAtkVersionAtLeast(2, 1)) {
    596    aAtkObj->role = ATK_ROLE_LIST;
    597  } else if (aAtkObj->role == ATK_ROLE_TABLE_ROW &&
    598             !IsAtkVersionAtLeast(2, 1)) {
    599    aAtkObj->role = ATK_ROLE_LIST_ITEM;
    600  } else if (aAtkObj->role == ATK_ROLE_MATH && !IsAtkVersionAtLeast(2, 12)) {
    601    aAtkObj->role = ATK_ROLE_SECTION;
    602  } else if (aAtkObj->role == ATK_ROLE_COMMENT && !IsAtkVersionAtLeast(2, 12)) {
    603    aAtkObj->role = ATK_ROLE_SECTION;
    604  } else if (aAtkObj->role == ATK_ROLE_LANDMARK &&
    605             !IsAtkVersionAtLeast(2, 12)) {
    606    aAtkObj->role = ATK_ROLE_SECTION;
    607  } else if (aAtkObj->role == ATK_ROLE_FOOTNOTE &&
    608             !IsAtkVersionAtLeast(2, 25, 2)) {
    609    aAtkObj->role = ATK_ROLE_SECTION;
    610  } else if (aAtkObj->role == ATK_ROLE_STATIC && !IsAtkVersionAtLeast(2, 16)) {
    611    aAtkObj->role = ATK_ROLE_TEXT;
    612  } else if ((aAtkObj->role == ATK_ROLE_MATH_FRACTION ||
    613              aAtkObj->role == ATK_ROLE_MATH_ROOT) &&
    614             !IsAtkVersionAtLeast(2, 16)) {
    615    aAtkObj->role = ATK_ROLE_SECTION;
    616  } else if (aAtkObj->role == ATK_ROLE_MARK && !IsAtkVersionAtLeast(2, 36)) {
    617    aAtkObj->role = ATK_ROLE_TEXT;
    618  } else if (aAtkObj->role == ATK_ROLE_SUGGESTION &&
    619             !IsAtkVersionAtLeast(2, 36)) {
    620    aAtkObj->role = ATK_ROLE_SECTION;
    621  } else if (aAtkObj->role == ATK_ROLE_COMMENT && !IsAtkVersionAtLeast(2, 36)) {
    622    aAtkObj->role = ATK_ROLE_SECTION;
    623  } else if ((aAtkObj->role == ATK_ROLE_CONTENT_DELETION ||
    624              aAtkObj->role == ATK_ROLE_CONTENT_INSERTION) &&
    625             !IsAtkVersionAtLeast(2, 34)) {
    626    aAtkObj->role = ATK_ROLE_SECTION;
    627  }
    628 
    629  return aAtkObj->role;
    630 }
    631 
    632 static AtkAttributeSet* ConvertToAtkAttributeSet(AccAttributes* aAttributes) {
    633  if (!aAttributes) {
    634    return nullptr;
    635  }
    636 
    637  AtkAttributeSet* objAttributeSet = nullptr;
    638 
    639  for (auto iter : *aAttributes) {
    640    nsAutoString name;
    641    iter.NameAsString(name);
    642    if (name.Equals(u"placeholder")) {
    643      name.AssignLiteral(u"placeholder-text");
    644    }
    645 
    646    nsAutoString value;
    647    iter.ValueAsString(value);
    648 
    649    AtkAttribute* objAttr = (AtkAttribute*)g_malloc(sizeof(AtkAttribute));
    650    objAttr->name = g_strdup(NS_ConvertUTF16toUTF8(name).get());
    651    objAttr->value = g_strdup(NS_ConvertUTF16toUTF8(value).get());
    652    objAttributeSet = g_slist_prepend(objAttributeSet, objAttr);
    653  }
    654 
    655  // libspi will free it
    656  return objAttributeSet;
    657 }
    658 
    659 AtkAttributeSet* getAttributesCB(AtkObject* aAtkObj) {
    660  Accessible* acc = GetInternalObj(aAtkObj);
    661  if (!acc) {
    662    return nullptr;
    663  }
    664  RefPtr<AccAttributes> attributes = acc->Attributes();
    665  nsAccUtils::SetAccGroupAttrs(attributes, acc);
    666  return ConvertToAtkAttributeSet(attributes);
    667 }
    668 
    669 const gchar* GetLocaleCB(AtkObject* aAtkObj) {
    670  Accessible* acc = GetInternalObj(aAtkObj);
    671  if (!acc) {
    672    return nullptr;
    673  }
    674 
    675  nsAutoString locale;
    676  acc->Language(locale);
    677  return AccessibleWrap::ReturnString(locale);
    678 }
    679 
    680 AtkObject* getParentCB(AtkObject* aAtkObj) {
    681  if (aAtkObj->accessible_parent) return aAtkObj->accessible_parent;
    682 
    683  Accessible* acc = GetInternalObj(aAtkObj);
    684  if (!acc) {
    685    return nullptr;
    686  }
    687 
    688  Accessible* parent = acc->Parent();
    689  AtkObject* atkParent = parent ? GetWrapperFor(parent) : nullptr;
    690  if (atkParent) atk_object_set_parent(aAtkObj, atkParent);
    691 
    692  return aAtkObj->accessible_parent;
    693 }
    694 
    695 gint getChildCountCB(AtkObject* aAtkObj) {
    696  Accessible* acc = GetInternalObj(aAtkObj);
    697  if (!acc || nsAccUtils::MustPrune(acc)) {
    698    return 0;
    699  }
    700  return static_cast<gint>(acc->EmbeddedChildCount());
    701 }
    702 
    703 AtkObject* refChildCB(AtkObject* aAtkObj, gint aChildIndex) {
    704  // aChildIndex should not be less than zero
    705  if (aChildIndex < 0) {
    706    return nullptr;
    707  }
    708 
    709  Accessible* acc = GetInternalObj(aAtkObj);
    710  if (!acc || nsAccUtils::MustPrune(acc)) {
    711    return nullptr;
    712  }
    713  Accessible* accChild = acc->EmbeddedChildAt(aChildIndex);
    714  if (!accChild) {
    715    return nullptr;
    716  }
    717 
    718  AtkObject* childAtkObj = GetWrapperFor(accChild);
    719  NS_ASSERTION(childAtkObj, "Fail to get AtkObj");
    720  if (!childAtkObj) {
    721    return nullptr;
    722  }
    723 
    724  g_object_ref(childAtkObj);
    725 
    726  if (aAtkObj != childAtkObj->accessible_parent) {
    727    atk_object_set_parent(childAtkObj, aAtkObj);
    728  }
    729 
    730  return childAtkObj;
    731 }
    732 
    733 gint getIndexInParentCB(AtkObject* aAtkObj) {
    734  // We don't use LocalAccessible::IndexInParent() because we don't include text
    735  // leaf nodes as children in ATK.
    736  Accessible* acc = GetInternalObj(aAtkObj);
    737  if (!acc) {
    738    return -1;
    739  }
    740  if (acc->IsDoc()) {
    741    return 0;
    742  }
    743  Accessible* parent = acc->Parent();
    744  if (!parent) {
    745    return -1;
    746  }
    747  return parent->IndexOfEmbeddedChild(acc);
    748 }
    749 
    750 static void TranslateStates(uint64_t aState, roles::Role aRole,
    751                            AtkStateSet* aStateSet) {
    752  // atk doesn't have a read only state so read only things shouldn't be
    753  // editable. However, we don't do this for list items because Gecko always
    754  // exposes those as read only.
    755  if ((aState & states::READONLY) && aRole != roles::LISTITEM) {
    756    aState &= ~states::EDITABLE;
    757  }
    758 
    759  // Convert every state to an entry in AtkStateMap
    760  uint64_t bitMask = 1;
    761  for (auto stateIndex = 0U; stateIndex < gAtkStateMapLen; stateIndex++) {
    762    if (gAtkStateMap[stateIndex]
    763            .atkState) {  // There's potentially an ATK state for this
    764      bool isStateOn = (aState & bitMask) != 0;
    765      if (gAtkStateMap[stateIndex].stateMapEntryType == kMapOpposite) {
    766        isStateOn = !isStateOn;
    767      }
    768      if (isStateOn) {
    769        atk_state_set_add_state(aStateSet, gAtkStateMap[stateIndex].atkState);
    770      }
    771    }
    772    bitMask <<= 1;
    773  }
    774 }
    775 
    776 AtkStateSet* refStateSetCB(AtkObject* aAtkObj) {
    777  AtkStateSet* state_set = nullptr;
    778  state_set = ATK_OBJECT_CLASS(parent_class)->ref_state_set(aAtkObj);
    779 
    780  if (Accessible* acc = GetInternalObj(aAtkObj)) {
    781    TranslateStates(acc->State(), acc->Role(), state_set);
    782  } else {
    783    TranslateStates(states::DEFUNCT, roles::NOTHING, state_set);
    784  }
    785 
    786  return state_set;
    787 }
    788 
    789 static void UpdateAtkRelation(RelationType aType, Accessible* aAcc,
    790                              AtkRelationType aAtkType,
    791                              AtkRelationSet* aAtkSet) {
    792  if (aAtkType == ATK_RELATION_NULL) return;
    793 
    794  AtkRelation* atkRelation =
    795      atk_relation_set_get_relation_by_type(aAtkSet, aAtkType);
    796  if (atkRelation) atk_relation_set_remove(aAtkSet, atkRelation);
    797 
    798  Relation rel(aAcc->RelationByType(aType));
    799  nsTArray<AtkObject*> targets;
    800  Accessible* tempAcc = nullptr;
    801  while ((tempAcc = rel.Next())) {
    802    targets.AppendElement(GetWrapperFor(tempAcc));
    803  }
    804 
    805  if (targets.Length()) {
    806    atkRelation =
    807        atk_relation_new(targets.Elements(), targets.Length(), aAtkType);
    808    atk_relation_set_add(aAtkSet, atkRelation);
    809    g_object_unref(atkRelation);
    810  }
    811 }
    812 
    813 AtkRelationSet* refRelationSetCB(AtkObject* aAtkObj) {
    814  AtkRelationSet* relation_set =
    815      ATK_OBJECT_CLASS(parent_class)->ref_relation_set(aAtkObj);
    816 
    817  Accessible* acc = GetInternalObj(aAtkObj);
    818  if (!acc) {
    819    return relation_set;
    820  }
    821 
    822 #define RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) \
    823  UpdateAtkRelation(RelationType::geckoType, acc, atkType, relation_set);
    824 
    825 #include "RelationTypeMap.h"
    826 
    827 #undef RELATIONTYPE
    828 
    829  return relation_set;
    830 }
    831 
    832 // Check if aAtkObj is a valid MaiAtkObject, and return the AccessibleWrap
    833 // for it.
    834 AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj) {
    835  NS_ENSURE_TRUE(IS_MAI_OBJECT(aAtkObj), nullptr);
    836 
    837  // If we're working with an ATK object, we need to convert the Accessible
    838  // back to an AccessibleWrap:
    839  Accessible* storedAcc = MAI_ATK_OBJECT(aAtkObj)->acc;
    840  if (!storedAcc) {
    841    return nullptr;
    842  }
    843  auto* accWrap = static_cast<AccessibleWrap*>(storedAcc->AsLocal());
    844 
    845  // Check if the accessible was deconstructed.
    846  if (!accWrap) return nullptr;
    847 
    848  NS_ENSURE_TRUE(accWrap->GetAtkObject() == aAtkObj, nullptr);
    849 
    850  AccessibleWrap* appAccWrap = ApplicationAcc();
    851  if (appAccWrap != accWrap && !accWrap->IsValidObject()) {
    852    return nullptr;
    853  }
    854 
    855  return accWrap;
    856 }
    857 
    858 RemoteAccessible* GetProxy(AtkObject* aObj) {
    859  Accessible* acc = GetInternalObj(aObj);
    860  if (!acc) {
    861    return nullptr;
    862  }
    863 
    864  return acc->AsRemote();
    865 }
    866 
    867 Accessible* GetInternalObj(AtkObject* aObj) {
    868  if (!aObj || !IS_MAI_OBJECT(aObj)) return nullptr;
    869 
    870  return MAI_ATK_OBJECT(aObj)->acc;
    871 }
    872 
    873 AtkObject* GetWrapperFor(Accessible* aAcc) {
    874  if (!aAcc) {
    875    return nullptr;
    876  }
    877 
    878  if (aAcc->IsRemote()) {
    879    return reinterpret_cast<AtkObject*>(aAcc->AsRemote()->GetWrapper());
    880  }
    881 
    882  return AccessibleWrap::GetAtkObject(aAcc->AsLocal());
    883 }
    884 
    885 void a11y::ProxyCreated(RemoteAccessible* aProxy) {
    886  MOZ_ASSERT(aProxy->RemoteParent() || aProxy->IsDoc(),
    887             "Need parent to check for HyperLink interface");
    888  GType type = GetMaiAtkType(CreateMaiInterfaces(aProxy));
    889  NS_ASSERTION(type, "why don't we have a type!");
    890 
    891  AtkObject* obj = reinterpret_cast<AtkObject*>(g_object_new(type, nullptr));
    892  if (!obj) return;
    893 
    894  atk_object_initialize(obj, static_cast<Accessible*>(aProxy));
    895  obj->role = ATK_ROLE_INVALID;
    896  obj->layer = ATK_LAYER_INVALID;
    897  aProxy->SetWrapper(reinterpret_cast<uintptr_t>(obj));
    898 }
    899 
    900 void a11y::ProxyDestroyed(RemoteAccessible* aProxy) {
    901  auto obj = reinterpret_cast<MaiAtkObject*>(aProxy->GetWrapper());
    902  if (!obj) {
    903    return;
    904  }
    905 
    906  obj->Shutdown();
    907  g_object_unref(obj);
    908  aProxy->SetWrapper(0);
    909 }
    910 
    911 void a11y::PlatformEvent(Accessible* aTarget, uint32_t aEventType) {
    912  AtkObject* wrapper = GetWrapperFor(aTarget);
    913 
    914  switch (aEventType) {
    915    case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE:
    916      if (aTarget->IsDoc()) {
    917        g_signal_emit_by_name(wrapper, "load_complete");
    918      }
    919      // XXX - Handle native dialog accessibles.
    920      if (!aTarget->IsRoot() && aTarget->HasARIARole() &&
    921          aTarget->Role() == roles::DIALOG) {
    922        guint id = g_signal_lookup("activate", MAI_TYPE_ATK_OBJECT);
    923        g_signal_emit(wrapper, id, 0);
    924      }
    925      break;
    926    case nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD:
    927      if (aTarget->IsDoc()) {
    928        g_signal_emit_by_name(wrapper, "reload");
    929      }
    930      break;
    931    case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED:
    932      if (aTarget->IsDoc()) {
    933        g_signal_emit_by_name(wrapper, "load_stopped");
    934      }
    935      break;
    936    case nsIAccessibleEvent::EVENT_MENUPOPUP_START:
    937      atk_focus_tracker_notify(wrapper);  // fire extra focus event
    938      atk_object_notify_state_change(wrapper, ATK_STATE_VISIBLE, true);
    939      atk_object_notify_state_change(wrapper, ATK_STATE_SHOWING, true);
    940      break;
    941    case nsIAccessibleEvent::EVENT_MENUPOPUP_END:
    942      atk_object_notify_state_change(wrapper, ATK_STATE_VISIBLE, false);
    943      atk_object_notify_state_change(wrapper, ATK_STATE_SHOWING, false);
    944      break;
    945    case nsIAccessibleEvent::EVENT_ALERT:
    946      // A hack using state change showing events as alert events.
    947      atk_object_notify_state_change(wrapper, ATK_STATE_SHOWING, true);
    948      break;
    949    case nsIAccessibleEvent::EVENT_VALUE_CHANGE:
    950    case nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE:
    951      if (aTarget->HasNumericValue()) {
    952        // Make sure this is a numeric value. Don't fire for string value
    953        // changes (e.g. text editing) ATK values are always numeric.
    954        g_object_notify((GObject*)wrapper, "accessible-value");
    955      }
    956      break;
    957    case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED:
    958      g_signal_emit_by_name(wrapper, "text_selection_changed");
    959      break;
    960    case nsIAccessibleEvent::EVENT_SELECTION_WITHIN:
    961      g_signal_emit_by_name(wrapper, "selection_changed");
    962      break;
    963    case nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED:
    964      g_signal_emit_by_name(wrapper, "text-attributes-changed");
    965      break;
    966    case nsIAccessibleEvent::EVENT_NAME_CHANGE: {
    967      // Don't want to passively activate the cache because a name changed.
    968      CacheDomainActivationBlocker cacheBlocker;
    969      nsAutoString newName;
    970      aTarget->Name(newName);
    971      MaybeFireNameChange(wrapper, newName);
    972      break;
    973    }
    974    case nsIAccessibleEvent::EVENT_WINDOW_ACTIVATE: {
    975      guint id = g_signal_lookup("activate", MAI_TYPE_ATK_OBJECT);
    976      g_signal_emit(wrapper, id, 0);
    977      // Always fire a current focus event after activation.
    978      FocusMgr()->ForceFocusEvent();
    979      break;
    980    }
    981    case nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE: {
    982      guint id = g_signal_lookup("deactivate", MAI_TYPE_ATK_OBJECT);
    983      g_signal_emit(wrapper, id, 0);
    984      break;
    985    }
    986    case nsIAccessibleEvent::EVENT_WINDOW_MAXIMIZE: {
    987      guint id = g_signal_lookup("maximize", MAI_TYPE_ATK_OBJECT);
    988      g_signal_emit(wrapper, id, 0);
    989      break;
    990    }
    991    case nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE: {
    992      guint id = g_signal_lookup("minimize", MAI_TYPE_ATK_OBJECT);
    993      g_signal_emit(wrapper, id, 0);
    994      break;
    995    }
    996    case nsIAccessibleEvent::EVENT_WINDOW_RESTORE: {
    997      guint id = g_signal_lookup("restore", MAI_TYPE_ATK_OBJECT);
    998      g_signal_emit(wrapper, id, 0);
    999      break;
   1000    }
   1001  }
   1002 }
   1003 
   1004 void a11y::PlatformStateChangeEvent(Accessible* aTarget, uint64_t aState,
   1005                                    bool aEnabled) {
   1006  MaiAtkObject* atkObj = MAI_ATK_OBJECT(GetWrapperFor(aTarget));
   1007  atkObj->FireStateChangeEvent(aState, aEnabled);
   1008 }
   1009 
   1010 void a11y::PlatformFocusEvent(Accessible* aTarget) {
   1011  AtkObject* wrapper = GetWrapperFor(aTarget);
   1012 
   1013  // XXX Do we really need this check? If so, do we need a similar check for
   1014  // RemoteAccessible?
   1015  if (LocalAccessible* localTarget = aTarget->AsLocal()) {
   1016    a11y::RootAccessible* rootAcc = localTarget->RootAccessible();
   1017    if (!rootAcc || !rootAcc->IsActivated()) {
   1018      return;
   1019    }
   1020  }
   1021 
   1022  atk_focus_tracker_notify(wrapper);
   1023  atk_object_notify_state_change(wrapper, ATK_STATE_FOCUSED, true);
   1024 }
   1025 
   1026 void a11y::PlatformCaretMoveEvent(Accessible* aTarget, int32_t aOffset,
   1027                                  bool aIsSelectionCollapsed,
   1028                                  int32_t aGranularity, bool aFromUser) {
   1029  AtkObject* wrapper = GetWrapperFor(aTarget);
   1030  g_signal_emit_by_name(wrapper, "text_caret_moved", aOffset);
   1031 }
   1032 
   1033 void MaiAtkObject::FireStateChangeEvent(uint64_t aState, bool aEnabled) {
   1034  auto state = aState;
   1035  int32_t stateIndex = -1;
   1036  while (state > 0) {
   1037    ++stateIndex;
   1038    state >>= 1;
   1039  }
   1040 
   1041  MOZ_ASSERT(
   1042      stateIndex >= 0 && stateIndex < static_cast<int32_t>(gAtkStateMapLen),
   1043      "No ATK state for internal state was found");
   1044  if (stateIndex < 0 || stateIndex >= static_cast<int32_t>(gAtkStateMapLen)) {
   1045    return;
   1046  }
   1047 
   1048  if (gAtkStateMap[stateIndex].atkState != kNone) {
   1049    MOZ_ASSERT(gAtkStateMap[stateIndex].stateMapEntryType != kNoStateChange,
   1050               "State changes should not fired for this state");
   1051 
   1052    if (gAtkStateMap[stateIndex].stateMapEntryType == kMapOpposite) {
   1053      aEnabled = !aEnabled;
   1054    }
   1055 
   1056    // Fire state change for first state if there is one to map
   1057    atk_object_notify_state_change(&parent, gAtkStateMap[stateIndex].atkState,
   1058                                   aEnabled);
   1059  }
   1060 }
   1061 
   1062 void a11y::PlatformTextChangeEvent(Accessible* aTarget, const nsAString& aStr,
   1063                                   int32_t aStart, uint32_t aLen,
   1064                                   bool aIsInsert, bool aFromUser) {
   1065  MaiAtkObject* atkObj = MAI_ATK_OBJECT(GetWrapperFor(aTarget));
   1066  atkObj->FireTextChangeEvent(aStr, aStart, aLen, aIsInsert, aFromUser);
   1067 }
   1068 
   1069 #define OLD_TEXT_INSERTED "text_changed::insert"
   1070 #define OLD_TEXT_REMOVED "text_changed::delete"
   1071 static const char* oldTextChangeStrings[2][2] = {
   1072    {OLD_TEXT_REMOVED NON_USER_EVENT, OLD_TEXT_INSERTED NON_USER_EVENT},
   1073    {OLD_TEXT_REMOVED, OLD_TEXT_INSERTED}};
   1074 
   1075 #define TEXT_INSERTED "text-insert"
   1076 #define TEXT_REMOVED "text-remove"
   1077 #define NON_USER_DETAIL "::system"
   1078 static const char* textChangedStrings[2][2] = {
   1079    {TEXT_REMOVED NON_USER_DETAIL, TEXT_INSERTED NON_USER_DETAIL},
   1080    {TEXT_REMOVED, TEXT_INSERTED}};
   1081 
   1082 void MaiAtkObject::FireTextChangeEvent(const nsAString& aStr, int32_t aStart,
   1083                                       uint32_t aLen, bool aIsInsert,
   1084                                       bool aFromUser) {
   1085  if (gAvailableAtkSignals == eUnknown) {
   1086    gAvailableAtkSignals = g_signal_lookup("text-insert", G_OBJECT_TYPE(this))
   1087                               ? eHaveNewAtkTextSignals
   1088                               : eNoNewAtkSignals;
   1089  }
   1090 
   1091  if (gAvailableAtkSignals == eNoNewAtkSignals) {
   1092    // XXX remove this code and the gHaveNewTextSignals check when we can
   1093    // stop supporting old atk since it doesn't really work anyway
   1094    // see bug 619002
   1095    const char* signal_name = oldTextChangeStrings[aFromUser][aIsInsert];
   1096    g_signal_emit_by_name(this, signal_name, aStart, aLen);
   1097  } else {
   1098    const char* signal_name = textChangedStrings[aFromUser][aIsInsert];
   1099    g_signal_emit_by_name(this, signal_name, aStart, aLen,
   1100                          NS_ConvertUTF16toUTF8(aStr).get());
   1101  }
   1102 }
   1103 
   1104 void a11y::PlatformShowHideEvent(Accessible* aTarget, Accessible* aParent,
   1105                                 bool aInsert, bool aFromUser) {
   1106  AtkObject* atkObj = GetWrapperFor(aTarget);
   1107  if (!aInsert) {
   1108    // XXX - Handle native dialog accessibles.
   1109    if (!aTarget->IsRoot() && aTarget->HasARIARole() &&
   1110        aTarget->Role() == roles::DIALOG) {
   1111      guint id = g_signal_lookup("deactivate", MAI_TYPE_ATK_OBJECT);
   1112      g_signal_emit(atkObj, id, 0);
   1113    }
   1114  }
   1115 
   1116  MaiAtkObject* obj = MAI_ATK_OBJECT(atkObj);
   1117  obj->FireAtkShowHideEvent(GetWrapperFor(aParent), aInsert, aFromUser);
   1118 }
   1119 
   1120 #define ADD_EVENT "children_changed::add"
   1121 #define HIDE_EVENT "children_changed::remove"
   1122 
   1123 static const char* kMutationStrings[2][2] = {
   1124    {HIDE_EVENT NON_USER_EVENT, ADD_EVENT NON_USER_EVENT},
   1125    {HIDE_EVENT, ADD_EVENT},
   1126 };
   1127 
   1128 void MaiAtkObject::FireAtkShowHideEvent(AtkObject* aParent, bool aIsAdded,
   1129                                        bool aFromUser) {
   1130  if (!aParent) {
   1131    // XXX ATK needs a parent for these events. However, we might have already
   1132    // unbound from the parent by the time we fire a hide event. Ideally, we
   1133    // need to find a way to keep the parent around, but ATK clients don't seem
   1134    // to care about these missing events.
   1135    MOZ_ASSERT(!aIsAdded);
   1136    return;
   1137  }
   1138  int32_t indexInParent = getIndexInParentCB(&this->parent);
   1139  const char* signal_name = kMutationStrings[aFromUser][aIsAdded];
   1140  g_signal_emit_by_name(aParent, signal_name, indexInParent, this, nullptr);
   1141 }
   1142 
   1143 void a11y::PlatformSelectionEvent(Accessible*, Accessible* aWidget, uint32_t) {
   1144  MaiAtkObject* obj = MAI_ATK_OBJECT(GetWrapperFor(aWidget));
   1145  g_signal_emit_by_name(obj, "selection_changed");
   1146 }
   1147 
   1148 mozilla::StaticAutoPtr<nsCString> sReturnedString;
   1149 
   1150 // static
   1151 const char* AccessibleWrap::ReturnString(nsAString& aString) {
   1152  if (!sReturnedString) {
   1153    sReturnedString = new nsCString();
   1154    ClearOnShutdown(&sReturnedString);
   1155  }
   1156 
   1157  CopyUTF16toUTF8(aString, *sReturnedString);
   1158  return sReturnedString->get();
   1159 }
   1160 
   1161 // static
   1162 void AccessibleWrap::GetKeyBinding(Accessible* aAccessible,
   1163                                   nsAString& aResult) {
   1164  // Return all key bindings including access key and keyboard shortcut.
   1165 
   1166  // Get access key.
   1167  nsAutoString keyBindingsStr;
   1168  KeyBinding keyBinding = aAccessible->AccessKey();
   1169  if (!keyBinding.IsEmpty()) {
   1170    keyBinding.AppendToString(keyBindingsStr, KeyBinding::eAtkFormat);
   1171 
   1172    Accessible* parent = aAccessible->Parent();
   1173    roles::Role role = parent ? parent->Role() : roles::NOTHING;
   1174    if (role == roles::PARENT_MENUITEM || role == roles::MENUITEM ||
   1175        role == roles::RADIO_MENU_ITEM || role == roles::CHECK_MENU_ITEM) {
   1176      // It is submenu, expose keyboard shortcuts from menu hierarchy like
   1177      // "s;<Alt>f:s"
   1178      nsAutoString keysInHierarchyStr = keyBindingsStr;
   1179      do {
   1180        KeyBinding parentKeyBinding = parent->AccessKey();
   1181        if (!parentKeyBinding.IsEmpty()) {
   1182          nsAutoString str;
   1183          parentKeyBinding.ToString(str, KeyBinding::eAtkFormat);
   1184          str.Append(':');
   1185 
   1186          keysInHierarchyStr.Insert(str, 0);
   1187        }
   1188      } while ((parent = parent->Parent()) && parent->Role() != roles::MENUBAR);
   1189 
   1190      keyBindingsStr.Append(';');
   1191      keyBindingsStr.Append(keysInHierarchyStr);
   1192    }
   1193  } else {
   1194    // No access key, add ';' to point this.
   1195    keyBindingsStr.Append(';');
   1196  }
   1197 
   1198  // Get keyboard shortcut.
   1199  keyBindingsStr.Append(';');
   1200  if (LocalAccessible* localAcc = aAccessible->AsLocal()) {
   1201    keyBinding = localAcc->KeyboardShortcut();
   1202    if (!keyBinding.IsEmpty()) {
   1203      keyBinding.AppendToString(keyBindingsStr, KeyBinding::eAtkFormat);
   1204    }
   1205  }
   1206  aResult = keyBindingsStr;
   1207 }
   1208 
   1209 // static
   1210 Accessible* AccessibleWrap::GetColumnHeader(TableAccessible* aAccessible,
   1211                                            int32_t aColIdx) {
   1212  if (!aAccessible) {
   1213    return nullptr;
   1214  }
   1215 
   1216  Accessible* cell = aAccessible->CellAt(0, aColIdx);
   1217  if (!cell) {
   1218    return nullptr;
   1219  }
   1220 
   1221  // If the cell at the first row is column header then assume it is column
   1222  // header for all rows,
   1223  if (cell->Role() == roles::COLUMNHEADER) {
   1224    return cell;
   1225  }
   1226 
   1227  // otherwise get column header for the data cell at the first row.
   1228  TableCellAccessible* tableCell = cell->AsTableCell();
   1229  if (!tableCell) {
   1230    return nullptr;
   1231  }
   1232 
   1233  AutoTArray<Accessible*, 10> headerCells;
   1234  tableCell->ColHeaderCells(&headerCells);
   1235  if (headerCells.IsEmpty()) {
   1236    return nullptr;
   1237  }
   1238 
   1239  return headerCells[0];
   1240 }
   1241 
   1242 // static
   1243 Accessible* AccessibleWrap::GetRowHeader(TableAccessible* aAccessible,
   1244                                         int32_t aRowIdx) {
   1245  if (!aAccessible) {
   1246    return nullptr;
   1247  }
   1248 
   1249  Accessible* cell = aAccessible->CellAt(aRowIdx, 0);
   1250  if (!cell) {
   1251    return nullptr;
   1252  }
   1253 
   1254  // If the cell at the first column is row header then assume it is row
   1255  // header for all columns,
   1256  if (cell->Role() == roles::ROWHEADER) {
   1257    return cell;
   1258  }
   1259 
   1260  // otherwise get row header for the data cell at the first column.
   1261  TableCellAccessible* tableCell = cell->AsTableCell();
   1262  if (!tableCell) {
   1263    return nullptr;
   1264  }
   1265 
   1266  AutoTArray<Accessible*, 10> headerCells;
   1267  tableCell->RowHeaderCells(&headerCells);
   1268  if (headerCells.IsEmpty()) {
   1269    return nullptr;
   1270  }
   1271 
   1272  return headerCells[0];
   1273 }