accessible/src/atk/AccessibleWrap.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/accessible/src/atk/AccessibleWrap.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1304 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "AccessibleWrap.h"
    1.11 +
    1.12 +#include "Accessible-inl.h"
    1.13 +#include "ApplicationAccessibleWrap.h"
    1.14 +#include "InterfaceInitFuncs.h"
    1.15 +#include "nsAccUtils.h"
    1.16 +#include "nsIAccessibleRelation.h"
    1.17 +#include "nsIAccessibleTable.h"
    1.18 +#include "RootAccessible.h"
    1.19 +#include "nsIAccessibleValue.h"
    1.20 +#include "nsMai.h"
    1.21 +#include "nsMaiHyperlink.h"
    1.22 +#include "nsString.h"
    1.23 +#include "nsAutoPtr.h"
    1.24 +#include "prprf.h"
    1.25 +#include "nsStateMap.h"
    1.26 +#include "Relation.h"
    1.27 +#include "RootAccessible.h"
    1.28 +#include "States.h"
    1.29 +#include "nsISimpleEnumerator.h"
    1.30 +
    1.31 +#include "mozilla/ArrayUtils.h"
    1.32 +#include "nsXPCOMStrings.h"
    1.33 +#include "nsComponentManagerUtils.h"
    1.34 +#include "nsIPersistentProperties2.h"
    1.35 +
    1.36 +using namespace mozilla;
    1.37 +using namespace mozilla::a11y;
    1.38 +
    1.39 +AccessibleWrap::EAvailableAtkSignals AccessibleWrap::gAvailableAtkSignals =
    1.40 +  eUnknown;
    1.41 +
    1.42 +//defined in ApplicationAccessibleWrap.cpp
    1.43 +extern "C" GType g_atk_hyperlink_impl_type;
    1.44 +
    1.45 +/* MaiAtkObject */
    1.46 +
    1.47 +enum {
    1.48 +  ACTIVATE,
    1.49 +  CREATE,
    1.50 +  DEACTIVATE,
    1.51 +  DESTROY,
    1.52 +  MAXIMIZE,
    1.53 +  MINIMIZE,
    1.54 +  RESIZE,
    1.55 +  RESTORE,
    1.56 +  LAST_SIGNAL
    1.57 +};
    1.58 +
    1.59 +enum MaiInterfaceType {
    1.60 +    MAI_INTERFACE_COMPONENT, /* 0 */
    1.61 +    MAI_INTERFACE_ACTION,
    1.62 +    MAI_INTERFACE_VALUE,
    1.63 +    MAI_INTERFACE_EDITABLE_TEXT,
    1.64 +    MAI_INTERFACE_HYPERTEXT,
    1.65 +    MAI_INTERFACE_HYPERLINK_IMPL,
    1.66 +    MAI_INTERFACE_SELECTION,
    1.67 +    MAI_INTERFACE_TABLE,
    1.68 +    MAI_INTERFACE_TEXT,
    1.69 +    MAI_INTERFACE_DOCUMENT, 
    1.70 +    MAI_INTERFACE_IMAGE /* 10 */
    1.71 +};
    1.72 +
    1.73 +static GType GetAtkTypeForMai(MaiInterfaceType type)
    1.74 +{
    1.75 +  switch (type) {
    1.76 +    case MAI_INTERFACE_COMPONENT:
    1.77 +      return ATK_TYPE_COMPONENT;
    1.78 +    case MAI_INTERFACE_ACTION:
    1.79 +      return ATK_TYPE_ACTION;
    1.80 +    case MAI_INTERFACE_VALUE:
    1.81 +      return ATK_TYPE_VALUE;
    1.82 +    case MAI_INTERFACE_EDITABLE_TEXT:
    1.83 +      return ATK_TYPE_EDITABLE_TEXT;
    1.84 +    case MAI_INTERFACE_HYPERTEXT:
    1.85 +      return ATK_TYPE_HYPERTEXT;
    1.86 +    case MAI_INTERFACE_HYPERLINK_IMPL:
    1.87 +       return g_atk_hyperlink_impl_type;
    1.88 +    case MAI_INTERFACE_SELECTION:
    1.89 +      return ATK_TYPE_SELECTION;
    1.90 +    case MAI_INTERFACE_TABLE:
    1.91 +      return ATK_TYPE_TABLE;
    1.92 +    case MAI_INTERFACE_TEXT:
    1.93 +      return ATK_TYPE_TEXT;
    1.94 +    case MAI_INTERFACE_DOCUMENT:
    1.95 +      return ATK_TYPE_DOCUMENT;
    1.96 +    case MAI_INTERFACE_IMAGE:
    1.97 +      return ATK_TYPE_IMAGE;
    1.98 +  }
    1.99 +  return G_TYPE_INVALID;
   1.100 +}
   1.101 +
   1.102 +static const char* kNonUserInputEvent = ":system";
   1.103 +    
   1.104 +static const GInterfaceInfo atk_if_infos[] = {
   1.105 +    {(GInterfaceInitFunc)componentInterfaceInitCB,
   1.106 +     (GInterfaceFinalizeFunc) nullptr, nullptr}, 
   1.107 +    {(GInterfaceInitFunc)actionInterfaceInitCB,
   1.108 +     (GInterfaceFinalizeFunc) nullptr, nullptr},
   1.109 +    {(GInterfaceInitFunc)valueInterfaceInitCB,
   1.110 +     (GInterfaceFinalizeFunc) nullptr, nullptr},
   1.111 +    {(GInterfaceInitFunc)editableTextInterfaceInitCB,
   1.112 +     (GInterfaceFinalizeFunc) nullptr, nullptr},
   1.113 +    {(GInterfaceInitFunc)hypertextInterfaceInitCB,
   1.114 +     (GInterfaceFinalizeFunc) nullptr, nullptr},
   1.115 +    {(GInterfaceInitFunc)hyperlinkImplInterfaceInitCB,
   1.116 +     (GInterfaceFinalizeFunc) nullptr, nullptr},
   1.117 +    {(GInterfaceInitFunc)selectionInterfaceInitCB,
   1.118 +     (GInterfaceFinalizeFunc) nullptr, nullptr},
   1.119 +    {(GInterfaceInitFunc)tableInterfaceInitCB,
   1.120 +     (GInterfaceFinalizeFunc) nullptr, nullptr},
   1.121 +    {(GInterfaceInitFunc)textInterfaceInitCB,
   1.122 +     (GInterfaceFinalizeFunc) nullptr, nullptr},
   1.123 +    {(GInterfaceInitFunc)documentInterfaceInitCB,
   1.124 +     (GInterfaceFinalizeFunc) nullptr, nullptr},
   1.125 +    {(GInterfaceInitFunc)imageInterfaceInitCB,
   1.126 +     (GInterfaceFinalizeFunc) nullptr, nullptr}
   1.127 +};
   1.128 +
   1.129 +/**
   1.130 + * This MaiAtkObject is a thin wrapper, in the MAI namespace, for AtkObject
   1.131 + */
   1.132 +struct MaiAtkObject
   1.133 +{
   1.134 +  AtkObject parent;
   1.135 +  /*
   1.136 +   * The AccessibleWrap whose properties and features are exported
   1.137 +   * via this object instance.
   1.138 +   */
   1.139 +  AccessibleWrap* accWrap;
   1.140 +};
   1.141 +
   1.142 +struct MaiAtkObjectClass
   1.143 +{
   1.144 +    AtkObjectClass parent_class;
   1.145 +};
   1.146 +
   1.147 +static guint mai_atk_object_signals [LAST_SIGNAL] = { 0, };
   1.148 +
   1.149 +static void MaybeFireNameChange(AtkObject* aAtkObj, const nsString& aNewName);
   1.150 +
   1.151 +G_BEGIN_DECLS
   1.152 +/* callbacks for MaiAtkObject */
   1.153 +static void classInitCB(AtkObjectClass *aClass);
   1.154 +static void initializeCB(AtkObject *aAtkObj, gpointer aData);
   1.155 +static void finalizeCB(GObject *aObj);
   1.156 +
   1.157 +/* callbacks for AtkObject virtual functions */
   1.158 +static const gchar*        getNameCB (AtkObject *aAtkObj);
   1.159 +/* getDescriptionCB is also used by image interface */
   1.160 +       const gchar*        getDescriptionCB (AtkObject *aAtkObj);
   1.161 +static AtkRole             getRoleCB(AtkObject *aAtkObj);
   1.162 +static AtkAttributeSet*    getAttributesCB(AtkObject *aAtkObj);
   1.163 +static const gchar* GetLocaleCB(AtkObject*);
   1.164 +static AtkObject*          getParentCB(AtkObject *aAtkObj);
   1.165 +static gint                getChildCountCB(AtkObject *aAtkObj);
   1.166 +static AtkObject*          refChildCB(AtkObject *aAtkObj, gint aChildIndex);
   1.167 +static gint                getIndexInParentCB(AtkObject *aAtkObj);
   1.168 +static AtkStateSet*        refStateSetCB(AtkObject *aAtkObj);
   1.169 +static AtkRelationSet*     refRelationSetCB(AtkObject *aAtkObj);
   1.170 +
   1.171 +/* the missing atkobject virtual functions */
   1.172 +/*
   1.173 +  static AtkLayer            getLayerCB(AtkObject *aAtkObj);
   1.174 +  static gint                getMdiZorderCB(AtkObject *aAtkObj);
   1.175 +  static void                SetNameCB(AtkObject *aAtkObj,
   1.176 +  const gchar *name);
   1.177 +  static void                SetDescriptionCB(AtkObject *aAtkObj,
   1.178 +  const gchar *description);
   1.179 +  static void                SetParentCB(AtkObject *aAtkObj,
   1.180 +  AtkObject *parent);
   1.181 +  static void                SetRoleCB(AtkObject *aAtkObj,
   1.182 +  AtkRole role);
   1.183 +  static guint               ConnectPropertyChangeHandlerCB(
   1.184 +  AtkObject  *aObj,
   1.185 +  AtkPropertyChangeHandler *handler);
   1.186 +  static void                RemovePropertyChangeHandlerCB(
   1.187 +  AtkObject *aAtkObj,
   1.188 +  guint handler_id);
   1.189 +  static void                InitializeCB(AtkObject *aAtkObj,
   1.190 +  gpointer data);
   1.191 +  static void                ChildrenChangedCB(AtkObject *aAtkObj,
   1.192 +  guint change_index,
   1.193 +  gpointer changed_child);
   1.194 +  static void                FocusEventCB(AtkObject *aAtkObj,
   1.195 +  gboolean focus_in);
   1.196 +  static void                PropertyChangeCB(AtkObject *aAtkObj,
   1.197 +  AtkPropertyValues *values);
   1.198 +  static void                StateChangeCB(AtkObject *aAtkObj,
   1.199 +  const gchar *name,
   1.200 +  gboolean state_set);
   1.201 +  static void                VisibleDataChangedCB(AtkObject *aAtkObj);
   1.202 +*/
   1.203 +G_END_DECLS
   1.204 +
   1.205 +static GType GetMaiAtkType(uint16_t interfacesBits);
   1.206 +static const char * GetUniqueMaiAtkTypeName(uint16_t interfacesBits);
   1.207 +
   1.208 +static gpointer parent_class = nullptr;
   1.209 +
   1.210 +static GQuark quark_mai_hyperlink = 0;
   1.211 +
   1.212 +GType
   1.213 +mai_atk_object_get_type(void)
   1.214 +{
   1.215 +    static GType type = 0;
   1.216 +
   1.217 +    if (!type) {
   1.218 +        static const GTypeInfo tinfo = {
   1.219 +            sizeof(MaiAtkObjectClass),
   1.220 +            (GBaseInitFunc)nullptr,
   1.221 +            (GBaseFinalizeFunc)nullptr,
   1.222 +            (GClassInitFunc)classInitCB,
   1.223 +            (GClassFinalizeFunc)nullptr,
   1.224 +            nullptr, /* class data */
   1.225 +            sizeof(MaiAtkObject), /* instance size */
   1.226 +            0, /* nb preallocs */
   1.227 +            (GInstanceInitFunc)nullptr,
   1.228 +            nullptr /* value table */
   1.229 +        };
   1.230 +
   1.231 +        type = g_type_register_static(ATK_TYPE_OBJECT,
   1.232 +                                      "MaiAtkObject", &tinfo, GTypeFlags(0));
   1.233 +        quark_mai_hyperlink = g_quark_from_static_string("MaiHyperlink");
   1.234 +    }
   1.235 +    return type;
   1.236 +}
   1.237 +
   1.238 +AccessibleWrap::
   1.239 +  AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) :
   1.240 +  Accessible(aContent, aDoc), mAtkObject(nullptr)
   1.241 +{
   1.242 +}
   1.243 +
   1.244 +AccessibleWrap::~AccessibleWrap()
   1.245 +{
   1.246 +    NS_ASSERTION(!mAtkObject, "ShutdownAtkObject() is not called");
   1.247 +}
   1.248 +
   1.249 +void
   1.250 +AccessibleWrap::ShutdownAtkObject()
   1.251 +{
   1.252 +    if (mAtkObject) {
   1.253 +        if (IS_MAI_OBJECT(mAtkObject)) {
   1.254 +            MAI_ATK_OBJECT(mAtkObject)->accWrap = nullptr;
   1.255 +        }
   1.256 +        SetMaiHyperlink(nullptr);
   1.257 +        g_object_unref(mAtkObject);
   1.258 +        mAtkObject = nullptr;
   1.259 +    }
   1.260 +}
   1.261 +
   1.262 +void
   1.263 +AccessibleWrap::Shutdown()
   1.264 +{
   1.265 +  ShutdownAtkObject();
   1.266 +  Accessible::Shutdown();
   1.267 +}
   1.268 +
   1.269 +MaiHyperlink*
   1.270 +AccessibleWrap::GetMaiHyperlink(bool aCreate /* = true */)
   1.271 +{
   1.272 +    // make sure mAtkObject is created
   1.273 +    GetAtkObject();
   1.274 +
   1.275 +    NS_ASSERTION(quark_mai_hyperlink, "quark_mai_hyperlink not initialized");
   1.276 +    NS_ASSERTION(IS_MAI_OBJECT(mAtkObject), "Invalid AtkObject");
   1.277 +    MaiHyperlink* maiHyperlink = nullptr;
   1.278 +    if (quark_mai_hyperlink && IS_MAI_OBJECT(mAtkObject)) {
   1.279 +        maiHyperlink = (MaiHyperlink*)g_object_get_qdata(G_OBJECT(mAtkObject),
   1.280 +                                                         quark_mai_hyperlink);
   1.281 +        if (!maiHyperlink && aCreate) {
   1.282 +            maiHyperlink = new MaiHyperlink(this);
   1.283 +            SetMaiHyperlink(maiHyperlink);
   1.284 +        }
   1.285 +    }
   1.286 +    return maiHyperlink;
   1.287 +}
   1.288 +
   1.289 +void
   1.290 +AccessibleWrap::SetMaiHyperlink(MaiHyperlink* aMaiHyperlink)
   1.291 +{
   1.292 +    NS_ASSERTION(quark_mai_hyperlink, "quark_mai_hyperlink not initialized");
   1.293 +    NS_ASSERTION(IS_MAI_OBJECT(mAtkObject), "Invalid AtkObject");
   1.294 +    if (quark_mai_hyperlink && IS_MAI_OBJECT(mAtkObject)) {
   1.295 +        MaiHyperlink* maiHyperlink = GetMaiHyperlink(false);
   1.296 +        if (!maiHyperlink && !aMaiHyperlink) {
   1.297 +            return; // Never set and we're shutting down
   1.298 +        }
   1.299 +        delete maiHyperlink;
   1.300 +        g_object_set_qdata(G_OBJECT(mAtkObject), quark_mai_hyperlink,
   1.301 +                           aMaiHyperlink);
   1.302 +    }
   1.303 +}
   1.304 +
   1.305 +NS_IMETHODIMP
   1.306 +AccessibleWrap::GetNativeInterface(void** aOutAccessible)
   1.307 +{
   1.308 +    *aOutAccessible = nullptr;
   1.309 +
   1.310 +    if (!mAtkObject) {
   1.311 +        if (IsDefunct() || !nsAccUtils::IsEmbeddedObject(this)) {
   1.312 +            // We don't create ATK objects for node which has been shutdown, or
   1.313 +            // nsIAccessible plain text leaves
   1.314 +            return NS_ERROR_FAILURE;
   1.315 +        }
   1.316 +
   1.317 +        GType type = GetMaiAtkType(CreateMaiInterfaces());
   1.318 +        NS_ENSURE_TRUE(type, NS_ERROR_FAILURE);
   1.319 +        mAtkObject =
   1.320 +            reinterpret_cast<AtkObject *>
   1.321 +                            (g_object_new(type, nullptr));
   1.322 +        NS_ENSURE_TRUE(mAtkObject, NS_ERROR_OUT_OF_MEMORY);
   1.323 +
   1.324 +        atk_object_initialize(mAtkObject, this);
   1.325 +        mAtkObject->role = ATK_ROLE_INVALID;
   1.326 +        mAtkObject->layer = ATK_LAYER_INVALID;
   1.327 +    }
   1.328 +
   1.329 +    *aOutAccessible = mAtkObject;
   1.330 +    return NS_OK;
   1.331 +}
   1.332 +
   1.333 +AtkObject *
   1.334 +AccessibleWrap::GetAtkObject(void)
   1.335 +{
   1.336 +    void *atkObj = nullptr;
   1.337 +    GetNativeInterface(&atkObj);
   1.338 +    return static_cast<AtkObject *>(atkObj);
   1.339 +}
   1.340 +
   1.341 +// Get AtkObject from nsIAccessible interface
   1.342 +/* static */
   1.343 +AtkObject *
   1.344 +AccessibleWrap::GetAtkObject(nsIAccessible* acc)
   1.345 +{
   1.346 +    void *atkObjPtr = nullptr;
   1.347 +    acc->GetNativeInterface(&atkObjPtr);
   1.348 +    return atkObjPtr ? ATK_OBJECT(atkObjPtr) : nullptr;    
   1.349 +}
   1.350 +
   1.351 +/* private */
   1.352 +uint16_t
   1.353 +AccessibleWrap::CreateMaiInterfaces(void)
   1.354 +{
   1.355 +  uint16_t interfacesBits = 0;
   1.356 +    
   1.357 +  // The Component interface is supported by all accessibles.
   1.358 +  interfacesBits |= 1 << MAI_INTERFACE_COMPONENT;
   1.359 +
   1.360 +  // Add Action interface if the action count is more than zero.
   1.361 +  if (ActionCount() > 0)
   1.362 +    interfacesBits |= 1 << MAI_INTERFACE_ACTION;
   1.363 +
   1.364 +  // Text, Editabletext, and Hypertext interface.
   1.365 +  HyperTextAccessible* hyperText = AsHyperText();
   1.366 +  if (hyperText && hyperText->IsTextRole()) {
   1.367 +    interfacesBits |= 1 << MAI_INTERFACE_TEXT;
   1.368 +    interfacesBits |= 1 << MAI_INTERFACE_EDITABLE_TEXT;
   1.369 +    if (!nsAccUtils::MustPrune(this))
   1.370 +      interfacesBits |= 1 << MAI_INTERFACE_HYPERTEXT;
   1.371 +  }
   1.372 +
   1.373 +  // Value interface.
   1.374 +  nsCOMPtr<nsIAccessibleValue> accessInterfaceValue;
   1.375 +  QueryInterface(NS_GET_IID(nsIAccessibleValue),
   1.376 +                 getter_AddRefs(accessInterfaceValue));
   1.377 +  if (accessInterfaceValue) {
   1.378 +    interfacesBits |= 1 << MAI_INTERFACE_VALUE; 
   1.379 +  }
   1.380 +
   1.381 +  // Document interface.
   1.382 +  if (IsDoc())
   1.383 +    interfacesBits |= 1 << MAI_INTERFACE_DOCUMENT;
   1.384 +
   1.385 +  if (IsImage())
   1.386 +    interfacesBits |= 1 << MAI_INTERFACE_IMAGE;
   1.387 +
   1.388 +  // HyperLink interface.
   1.389 +  if (IsLink())
   1.390 +    interfacesBits |= 1 << MAI_INTERFACE_HYPERLINK_IMPL;
   1.391 +
   1.392 +  if (!nsAccUtils::MustPrune(this)) {  // These interfaces require children
   1.393 +    // Table interface.
   1.394 +    if (AsTable())
   1.395 +      interfacesBits |= 1 << MAI_INTERFACE_TABLE;
   1.396 + 
   1.397 +    // Selection interface.
   1.398 +    if (IsSelect()) {
   1.399 +      interfacesBits |= 1 << MAI_INTERFACE_SELECTION;
   1.400 +    }
   1.401 +  }
   1.402 +
   1.403 +  return interfacesBits;
   1.404 +}
   1.405 +
   1.406 +static GType
   1.407 +GetMaiAtkType(uint16_t interfacesBits)
   1.408 +{
   1.409 +    GType type;
   1.410 +    static const GTypeInfo tinfo = {
   1.411 +        sizeof(MaiAtkObjectClass),
   1.412 +        (GBaseInitFunc) nullptr,
   1.413 +        (GBaseFinalizeFunc) nullptr,
   1.414 +        (GClassInitFunc) nullptr,
   1.415 +        (GClassFinalizeFunc) nullptr,
   1.416 +        nullptr, /* class data */
   1.417 +        sizeof(MaiAtkObject), /* instance size */
   1.418 +        0, /* nb preallocs */
   1.419 +        (GInstanceInitFunc) nullptr,
   1.420 +        nullptr /* value table */
   1.421 +    };
   1.422 +
   1.423 +    /*
   1.424 +     * The members we use to register GTypes are GetAtkTypeForMai
   1.425 +     * and atk_if_infos, which are constant values to each MaiInterface
   1.426 +     * So we can reuse the registered GType when having
   1.427 +     * the same MaiInterface types.
   1.428 +     */
   1.429 +    const char *atkTypeName = GetUniqueMaiAtkTypeName(interfacesBits);
   1.430 +    type = g_type_from_name(atkTypeName);
   1.431 +    if (type) {
   1.432 +        return type;
   1.433 +    }
   1.434 +
   1.435 +    /*
   1.436 +     * gobject limits the number of types that can directly derive from any
   1.437 +     * given object type to 4095.
   1.438 +     */
   1.439 +    static uint16_t typeRegCount = 0;
   1.440 +    if (typeRegCount++ >= 4095) {
   1.441 +        return G_TYPE_INVALID;
   1.442 +    }
   1.443 +    type = g_type_register_static(MAI_TYPE_ATK_OBJECT,
   1.444 +                                  atkTypeName,
   1.445 +                                  &tinfo, GTypeFlags(0));
   1.446 +
   1.447 +    for (uint32_t index = 0; index < ArrayLength(atk_if_infos); index++) {
   1.448 +      if (interfacesBits & (1 << index)) {
   1.449 +        g_type_add_interface_static(type,
   1.450 +                                    GetAtkTypeForMai((MaiInterfaceType)index),
   1.451 +                                    &atk_if_infos[index]);
   1.452 +      }
   1.453 +    }
   1.454 +
   1.455 +    return type;
   1.456 +}
   1.457 +
   1.458 +static const char*
   1.459 +GetUniqueMaiAtkTypeName(uint16_t interfacesBits)
   1.460 +{
   1.461 +#define MAI_ATK_TYPE_NAME_LEN (30)     /* 10+sizeof(uint16_t)*8/4+1 < 30 */
   1.462 +
   1.463 +    static gchar namePrefix[] = "MaiAtkType";   /* size = 10 */
   1.464 +    static gchar name[MAI_ATK_TYPE_NAME_LEN + 1];
   1.465 +
   1.466 +    PR_snprintf(name, MAI_ATK_TYPE_NAME_LEN, "%s%x", namePrefix,
   1.467 +                interfacesBits);
   1.468 +    name[MAI_ATK_TYPE_NAME_LEN] = '\0';
   1.469 +
   1.470 +    return name;
   1.471 +}
   1.472 +
   1.473 +bool
   1.474 +AccessibleWrap::IsValidObject()
   1.475 +{
   1.476 +    // to ensure we are not shut down
   1.477 +    return !IsDefunct();
   1.478 +}
   1.479 +
   1.480 +/* static functions for ATK callbacks */
   1.481 +void
   1.482 +classInitCB(AtkObjectClass *aClass)
   1.483 +{
   1.484 +    GObjectClass *gobject_class = G_OBJECT_CLASS(aClass);
   1.485 +
   1.486 +    parent_class = g_type_class_peek_parent(aClass);
   1.487 +
   1.488 +    aClass->get_name = getNameCB;
   1.489 +    aClass->get_description = getDescriptionCB;
   1.490 +    aClass->get_parent = getParentCB;
   1.491 +    aClass->get_n_children = getChildCountCB;
   1.492 +    aClass->ref_child = refChildCB;
   1.493 +    aClass->get_index_in_parent = getIndexInParentCB;
   1.494 +    aClass->get_role = getRoleCB;
   1.495 +    aClass->get_attributes = getAttributesCB;
   1.496 +    aClass->get_object_locale = GetLocaleCB;
   1.497 +    aClass->ref_state_set = refStateSetCB;
   1.498 +    aClass->ref_relation_set = refRelationSetCB;
   1.499 +
   1.500 +    aClass->initialize = initializeCB;
   1.501 +
   1.502 +    gobject_class->finalize = finalizeCB;
   1.503 +
   1.504 +    mai_atk_object_signals [ACTIVATE] =
   1.505 +    g_signal_new ("activate",
   1.506 +                  MAI_TYPE_ATK_OBJECT,
   1.507 +                  G_SIGNAL_RUN_LAST,
   1.508 +                  0, /* default signal handler */
   1.509 +                  nullptr, nullptr,
   1.510 +                  g_cclosure_marshal_VOID__VOID,
   1.511 +                  G_TYPE_NONE, 0);
   1.512 +    mai_atk_object_signals [CREATE] =
   1.513 +    g_signal_new ("create",
   1.514 +                  MAI_TYPE_ATK_OBJECT,
   1.515 +                  G_SIGNAL_RUN_LAST,
   1.516 +                  0, /* default signal handler */
   1.517 +                  nullptr, nullptr,
   1.518 +                  g_cclosure_marshal_VOID__VOID,
   1.519 +                  G_TYPE_NONE, 0);
   1.520 +    mai_atk_object_signals [DEACTIVATE] =
   1.521 +    g_signal_new ("deactivate",
   1.522 +                  MAI_TYPE_ATK_OBJECT,
   1.523 +                  G_SIGNAL_RUN_LAST,
   1.524 +                  0, /* default signal handler */
   1.525 +                  nullptr, nullptr,
   1.526 +                  g_cclosure_marshal_VOID__VOID,
   1.527 +                  G_TYPE_NONE, 0);
   1.528 +    mai_atk_object_signals [DESTROY] =
   1.529 +    g_signal_new ("destroy",
   1.530 +                  MAI_TYPE_ATK_OBJECT,
   1.531 +                  G_SIGNAL_RUN_LAST,
   1.532 +                  0, /* default signal handler */
   1.533 +                  nullptr, nullptr,
   1.534 +                  g_cclosure_marshal_VOID__VOID,
   1.535 +                  G_TYPE_NONE, 0);
   1.536 +    mai_atk_object_signals [MAXIMIZE] =
   1.537 +    g_signal_new ("maximize",
   1.538 +                  MAI_TYPE_ATK_OBJECT,
   1.539 +                  G_SIGNAL_RUN_LAST,
   1.540 +                  0, /* default signal handler */
   1.541 +                  nullptr, nullptr,
   1.542 +                  g_cclosure_marshal_VOID__VOID,
   1.543 +                  G_TYPE_NONE, 0);
   1.544 +    mai_atk_object_signals [MINIMIZE] =
   1.545 +    g_signal_new ("minimize",
   1.546 +                  MAI_TYPE_ATK_OBJECT,
   1.547 +                  G_SIGNAL_RUN_LAST,
   1.548 +                  0, /* default signal handler */
   1.549 +                  nullptr, nullptr,
   1.550 +                  g_cclosure_marshal_VOID__VOID,
   1.551 +                  G_TYPE_NONE, 0);
   1.552 +    mai_atk_object_signals [RESIZE] =
   1.553 +    g_signal_new ("resize",
   1.554 +                  MAI_TYPE_ATK_OBJECT,
   1.555 +                  G_SIGNAL_RUN_LAST,
   1.556 +                  0, /* default signal handler */
   1.557 +                  nullptr, nullptr,
   1.558 +                  g_cclosure_marshal_VOID__VOID,
   1.559 +                  G_TYPE_NONE, 0);
   1.560 +    mai_atk_object_signals [RESTORE] =
   1.561 +    g_signal_new ("restore",
   1.562 +                  MAI_TYPE_ATK_OBJECT,
   1.563 +                  G_SIGNAL_RUN_LAST,
   1.564 +                  0, /* default signal handler */
   1.565 +                  nullptr, nullptr,
   1.566 +                  g_cclosure_marshal_VOID__VOID,
   1.567 +                  G_TYPE_NONE, 0);
   1.568 +
   1.569 +}
   1.570 +
   1.571 +void
   1.572 +initializeCB(AtkObject *aAtkObj, gpointer aData)
   1.573 +{
   1.574 +    NS_ASSERTION((IS_MAI_OBJECT(aAtkObj)), "Invalid AtkObject");
   1.575 +    NS_ASSERTION(aData, "Invalid Data to init AtkObject");
   1.576 +    if (!aAtkObj || !aData)
   1.577 +        return;
   1.578 +
   1.579 +    /* call parent init function */
   1.580 +    /* AtkObjectClass has not a "initialize" function now,
   1.581 +     * maybe it has later
   1.582 +     */
   1.583 +
   1.584 +    if (ATK_OBJECT_CLASS(parent_class)->initialize)
   1.585 +        ATK_OBJECT_CLASS(parent_class)->initialize(aAtkObj, aData);
   1.586 +
   1.587 +  /* initialize object */
   1.588 +  MAI_ATK_OBJECT(aAtkObj)->accWrap =
   1.589 +    static_cast<AccessibleWrap*>(aData);
   1.590 +}
   1.591 +
   1.592 +void
   1.593 +finalizeCB(GObject *aObj)
   1.594 +{
   1.595 +    if (!IS_MAI_OBJECT(aObj))
   1.596 +        return;
   1.597 +    NS_ASSERTION(MAI_ATK_OBJECT(aObj)->accWrap == nullptr, "AccWrap NOT null");
   1.598 +
   1.599 +    // call parent finalize function
   1.600 +    // finalize of GObjectClass will unref the accessible parent if has
   1.601 +    if (G_OBJECT_CLASS (parent_class)->finalize)
   1.602 +        G_OBJECT_CLASS (parent_class)->finalize(aObj);
   1.603 +}
   1.604 +
   1.605 +const gchar*
   1.606 +getNameCB(AtkObject* aAtkObj)
   1.607 +{
   1.608 +  AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
   1.609 +  if (!accWrap)
   1.610 +    return nullptr;
   1.611 +
   1.612 +  nsAutoString name;
   1.613 +  accWrap->Name(name);
   1.614 +
   1.615 +  // XXX Firing an event from here does not seem right
   1.616 +  MaybeFireNameChange(aAtkObj, name);
   1.617 +
   1.618 +  return aAtkObj->name;
   1.619 +}
   1.620 +
   1.621 +static void
   1.622 +MaybeFireNameChange(AtkObject* aAtkObj, const nsString& aNewName)
   1.623 +{
   1.624 +  NS_ConvertUTF16toUTF8 newNameUTF8(aNewName);
   1.625 +  if (aAtkObj->name && newNameUTF8.Equals(aAtkObj->name))
   1.626 +    return;
   1.627 +
   1.628 +  // Below we duplicate the functionality of atk_object_set_name(),
   1.629 +  // but without calling atk_object_get_name(). Instead of
   1.630 +  // atk_object_get_name() we directly access aAtkObj->name. This is because
   1.631 +  // atk_object_get_name() would call getNameCB() which would call
   1.632 +  // MaybeFireNameChange() (or atk_object_set_name() before this problem was
   1.633 +  // fixed) and we would get an infinite recursion.
   1.634 +  // See http://bugzilla.mozilla.org/733712
   1.635 +
   1.636 +  // Do not notify for initial name setting.
   1.637 +  // See bug http://bugzilla.gnome.org/665870
   1.638 +  bool notify = !!aAtkObj->name;
   1.639 +
   1.640 +  free(aAtkObj->name);
   1.641 +  aAtkObj->name = strdup(newNameUTF8.get());
   1.642 +
   1.643 +  if (notify)
   1.644 +    g_object_notify(G_OBJECT(aAtkObj), "accessible-name");
   1.645 +}
   1.646 +
   1.647 +const gchar *
   1.648 +getDescriptionCB(AtkObject *aAtkObj)
   1.649 +{
   1.650 +    AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
   1.651 +    if (!accWrap || accWrap->IsDefunct())
   1.652 +        return nullptr;
   1.653 +
   1.654 +    /* nsIAccessible is responsible for the nonnull description */
   1.655 +    nsAutoString uniDesc;
   1.656 +    accWrap->Description(uniDesc);
   1.657 +
   1.658 +    NS_ConvertUTF8toUTF16 objDesc(aAtkObj->description);
   1.659 +    if (!uniDesc.Equals(objDesc))
   1.660 +        atk_object_set_description(aAtkObj,
   1.661 +                                   NS_ConvertUTF16toUTF8(uniDesc).get());
   1.662 +
   1.663 +    return aAtkObj->description;
   1.664 +}
   1.665 +
   1.666 +AtkRole
   1.667 +getRoleCB(AtkObject *aAtkObj)
   1.668 +{
   1.669 +  AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
   1.670 +  if (!accWrap)
   1.671 +    return ATK_ROLE_INVALID;
   1.672 +
   1.673 +#ifdef DEBUG
   1.674 +  NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(accWrap),
   1.675 +      "Does not support nsIAccessibleText when it should");
   1.676 +#endif
   1.677 +
   1.678 +  if (aAtkObj->role != ATK_ROLE_INVALID)
   1.679 +    return aAtkObj->role;
   1.680 +
   1.681 +#define ROLE(geckoRole, stringRole, atkRole, macRole, \
   1.682 +             msaaRole, ia2Role, nameRule) \
   1.683 +  case roles::geckoRole: \
   1.684 +    aAtkObj->role = atkRole; \
   1.685 +    break;
   1.686 +
   1.687 +  switch (accWrap->Role()) {
   1.688 +#include "RoleMap.h"
   1.689 +    default:
   1.690 +      MOZ_CRASH("Unknown role.");
   1.691 +  };
   1.692 +
   1.693 +#undef ROLE
   1.694 +
   1.695 +  if (aAtkObj->role == ATK_ROLE_LIST_BOX && !IsAtkVersionAtLeast(2, 1))
   1.696 +    aAtkObj->role = ATK_ROLE_LIST;
   1.697 +  else if (aAtkObj->role == ATK_ROLE_TABLE_ROW && !IsAtkVersionAtLeast(2, 1))
   1.698 +    aAtkObj->role = ATK_ROLE_LIST_ITEM;
   1.699 +
   1.700 +  return aAtkObj->role;
   1.701 +}
   1.702 +
   1.703 +static AtkAttributeSet*
   1.704 +ConvertToAtkAttributeSet(nsIPersistentProperties* aAttributes)
   1.705 +{
   1.706 +    if (!aAttributes)
   1.707 +        return nullptr;
   1.708 +
   1.709 +    AtkAttributeSet *objAttributeSet = nullptr;
   1.710 +    nsCOMPtr<nsISimpleEnumerator> propEnum;
   1.711 +    nsresult rv = aAttributes->Enumerate(getter_AddRefs(propEnum));
   1.712 +    NS_ENSURE_SUCCESS(rv, nullptr);
   1.713 +
   1.714 +    bool hasMore;
   1.715 +    while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) {
   1.716 +        nsCOMPtr<nsISupports> sup;
   1.717 +        rv = propEnum->GetNext(getter_AddRefs(sup));
   1.718 +        NS_ENSURE_SUCCESS(rv, objAttributeSet);
   1.719 +
   1.720 +        nsCOMPtr<nsIPropertyElement> propElem(do_QueryInterface(sup));
   1.721 +        NS_ENSURE_TRUE(propElem, objAttributeSet);
   1.722 +
   1.723 +        nsAutoCString name;
   1.724 +        rv = propElem->GetKey(name);
   1.725 +        NS_ENSURE_SUCCESS(rv, objAttributeSet);
   1.726 +
   1.727 +        nsAutoString value;
   1.728 +        rv = propElem->GetValue(value);
   1.729 +        NS_ENSURE_SUCCESS(rv, objAttributeSet);
   1.730 +
   1.731 +        AtkAttribute *objAttr = (AtkAttribute *)g_malloc(sizeof(AtkAttribute));
   1.732 +        objAttr->name = g_strdup(name.get());
   1.733 +        objAttr->value = g_strdup(NS_ConvertUTF16toUTF8(value).get());
   1.734 +        objAttributeSet = g_slist_prepend(objAttributeSet, objAttr);
   1.735 +    }
   1.736 +
   1.737 +    //libspi will free it
   1.738 +    return objAttributeSet;
   1.739 +}
   1.740 +
   1.741 +AtkAttributeSet*
   1.742 +GetAttributeSet(Accessible* aAccessible)
   1.743 +{
   1.744 +  nsCOMPtr<nsIPersistentProperties> attributes = aAccessible->Attributes();
   1.745 +  if (attributes) {
   1.746 +    // There is no ATK state for haspopup, must use object attribute to expose
   1.747 +    // the same info.
   1.748 +    if (aAccessible->State() & states::HASPOPUP) {
   1.749 +      nsAutoString unused;
   1.750 +      attributes->SetStringProperty(NS_LITERAL_CSTRING("haspopup"),
   1.751 +                                    NS_LITERAL_STRING("true"), unused);
   1.752 +    }
   1.753 +
   1.754 +    return ConvertToAtkAttributeSet(attributes);
   1.755 +  }
   1.756 +
   1.757 +  return nullptr;
   1.758 +}
   1.759 +
   1.760 +AtkAttributeSet *
   1.761 +getAttributesCB(AtkObject *aAtkObj)
   1.762 +{
   1.763 +  AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
   1.764 +  return accWrap ? GetAttributeSet(accWrap) : nullptr;
   1.765 +}
   1.766 +
   1.767 +const gchar*
   1.768 +GetLocaleCB(AtkObject* aAtkObj)
   1.769 +{
   1.770 +  AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
   1.771 +  if (!accWrap)
   1.772 +    return nullptr;
   1.773 +
   1.774 +  nsAutoString locale;
   1.775 +  accWrap->Language(locale);
   1.776 +  return AccessibleWrap::ReturnString(locale);
   1.777 +}
   1.778 +
   1.779 +AtkObject *
   1.780 +getParentCB(AtkObject *aAtkObj)
   1.781 +{
   1.782 +  if (!aAtkObj->accessible_parent) {
   1.783 +    AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
   1.784 +    if (!accWrap)
   1.785 +      return nullptr;
   1.786 +
   1.787 +    Accessible* accParent = accWrap->Parent();
   1.788 +    if (!accParent)
   1.789 +      return nullptr;
   1.790 +
   1.791 +    AtkObject* parent = AccessibleWrap::GetAtkObject(accParent);
   1.792 +    if (parent)
   1.793 +      atk_object_set_parent(aAtkObj, parent);
   1.794 +  }
   1.795 +  return aAtkObj->accessible_parent;
   1.796 +}
   1.797 +
   1.798 +gint
   1.799 +getChildCountCB(AtkObject *aAtkObj)
   1.800 +{
   1.801 +    AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
   1.802 +    if (!accWrap || nsAccUtils::MustPrune(accWrap)) {
   1.803 +        return 0;
   1.804 +    }
   1.805 +
   1.806 +    return static_cast<gint>(accWrap->EmbeddedChildCount());
   1.807 +}
   1.808 +
   1.809 +AtkObject *
   1.810 +refChildCB(AtkObject *aAtkObj, gint aChildIndex)
   1.811 +{
   1.812 +    // aChildIndex should not be less than zero
   1.813 +    if (aChildIndex < 0) {
   1.814 +      return nullptr;
   1.815 +    }
   1.816 +
   1.817 +    AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
   1.818 +    if (!accWrap || nsAccUtils::MustPrune(accWrap)) {
   1.819 +        return nullptr;
   1.820 +    }
   1.821 +
   1.822 +    Accessible* accChild = accWrap->GetEmbeddedChildAt(aChildIndex);
   1.823 +    if (!accChild)
   1.824 +        return nullptr;
   1.825 +
   1.826 +    AtkObject* childAtkObj = AccessibleWrap::GetAtkObject(accChild);
   1.827 +
   1.828 +    NS_ASSERTION(childAtkObj, "Fail to get AtkObj");
   1.829 +    if (!childAtkObj)
   1.830 +        return nullptr;
   1.831 +    g_object_ref(childAtkObj);
   1.832 +
   1.833 +  if (aAtkObj != childAtkObj->accessible_parent)
   1.834 +    atk_object_set_parent(childAtkObj, aAtkObj);
   1.835 +
   1.836 +  return childAtkObj;
   1.837 +}
   1.838 +
   1.839 +gint
   1.840 +getIndexInParentCB(AtkObject *aAtkObj)
   1.841 +{
   1.842 +    // We don't use nsIAccessible::GetIndexInParent() because
   1.843 +    // for ATK we don't want to include text leaf nodes as children
   1.844 +    AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
   1.845 +    if (!accWrap) {
   1.846 +        return -1;
   1.847 +    }
   1.848 +
   1.849 +    Accessible* parent = accWrap->Parent();
   1.850 +    if (!parent)
   1.851 +        return -1; // No parent
   1.852 +
   1.853 +    return parent->GetIndexOfEmbeddedChild(accWrap);
   1.854 +}
   1.855 +
   1.856 +static void
   1.857 +TranslateStates(uint64_t aState, AtkStateSet* aStateSet)
   1.858 +{
   1.859 +  // atk doesn't have a read only state so read only things shouldn't be
   1.860 +  // editable.
   1.861 +  if (aState & states::READONLY)
   1.862 +    aState &= ~states::EDITABLE;
   1.863 +
   1.864 +  // Convert every state to an entry in AtkStateMap
   1.865 +  uint32_t stateIndex = 0;
   1.866 +  uint64_t bitMask = 1;
   1.867 +  while (gAtkStateMap[stateIndex].stateMapEntryType != kNoSuchState) {
   1.868 +    if (gAtkStateMap[stateIndex].atkState) { // There's potentially an ATK state for this
   1.869 +      bool isStateOn = (aState & bitMask) != 0;
   1.870 +      if (gAtkStateMap[stateIndex].stateMapEntryType == kMapOpposite) {
   1.871 +        isStateOn = !isStateOn;
   1.872 +      }
   1.873 +      if (isStateOn) {
   1.874 +        atk_state_set_add_state(aStateSet, gAtkStateMap[stateIndex].atkState);
   1.875 +      }
   1.876 +    }
   1.877 +    bitMask <<= 1;
   1.878 +    ++ stateIndex;
   1.879 +  }
   1.880 +}
   1.881 +
   1.882 +AtkStateSet *
   1.883 +refStateSetCB(AtkObject *aAtkObj)
   1.884 +{
   1.885 +    AtkStateSet *state_set = nullptr;
   1.886 +    state_set = ATK_OBJECT_CLASS(parent_class)->ref_state_set(aAtkObj);
   1.887 +
   1.888 +    AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
   1.889 +    if (!accWrap) {
   1.890 +        TranslateStates(states::DEFUNCT, state_set);
   1.891 +        return state_set;
   1.892 +    }
   1.893 +
   1.894 +    // Map states
   1.895 +    TranslateStates(accWrap->State(), state_set);
   1.896 +
   1.897 +    return state_set;
   1.898 +}
   1.899 +
   1.900 +static void
   1.901 +UpdateAtkRelation(RelationType aType, Accessible* aAcc,
   1.902 +                  AtkRelationType aAtkType, AtkRelationSet* aAtkSet)
   1.903 +{
   1.904 +  if (aAtkType == ATK_RELATION_NULL)
   1.905 +    return;
   1.906 +
   1.907 +  AtkRelation* atkRelation =
   1.908 +    atk_relation_set_get_relation_by_type(aAtkSet, aAtkType);
   1.909 +  if (atkRelation)
   1.910 +    atk_relation_set_remove(aAtkSet, atkRelation);
   1.911 +
   1.912 +  Relation rel(aAcc->RelationByType(aType));
   1.913 +  nsTArray<AtkObject*> targets;
   1.914 +  Accessible* tempAcc = nullptr;
   1.915 +  while ((tempAcc = rel.Next()))
   1.916 +    targets.AppendElement(AccessibleWrap::GetAtkObject(tempAcc));
   1.917 +
   1.918 +  if (targets.Length()) {
   1.919 +    atkRelation = atk_relation_new(targets.Elements(),
   1.920 +                                   targets.Length(), aAtkType);
   1.921 +    atk_relation_set_add(aAtkSet, atkRelation);
   1.922 +    g_object_unref(atkRelation);
   1.923 +  }
   1.924 +}
   1.925 +
   1.926 +AtkRelationSet *
   1.927 +refRelationSetCB(AtkObject *aAtkObj)
   1.928 +{
   1.929 +  AtkRelationSet* relation_set =
   1.930 +    ATK_OBJECT_CLASS(parent_class)->ref_relation_set(aAtkObj);
   1.931 +
   1.932 +  AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
   1.933 +  if (!accWrap)
   1.934 +    return relation_set;
   1.935 +
   1.936 +#define RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) \
   1.937 +  UpdateAtkRelation(RelationType::geckoType, accWrap, atkType, relation_set);
   1.938 +
   1.939 +#include "RelationTypeMap.h"
   1.940 +
   1.941 +#undef RELATIONTYPE
   1.942 +
   1.943 +  return relation_set;
   1.944 +}
   1.945 +
   1.946 +// Check if aAtkObj is a valid MaiAtkObject, and return the AccessibleWrap
   1.947 +// for it.
   1.948 +AccessibleWrap*
   1.949 +GetAccessibleWrap(AtkObject* aAtkObj)
   1.950 +{
   1.951 +  NS_ENSURE_TRUE(IS_MAI_OBJECT(aAtkObj), nullptr);
   1.952 +  AccessibleWrap* accWrap = MAI_ATK_OBJECT(aAtkObj)->accWrap;
   1.953 +
   1.954 +  // Check if the accessible was deconstructed.
   1.955 +  if (!accWrap)
   1.956 +    return nullptr;
   1.957 +
   1.958 +  NS_ENSURE_TRUE(accWrap->GetAtkObject() == aAtkObj, nullptr);
   1.959 +
   1.960 +  AccessibleWrap* appAccWrap = ApplicationAcc();
   1.961 +  if (appAccWrap != accWrap && !accWrap->IsValidObject())
   1.962 +    return nullptr;
   1.963 +
   1.964 +  return accWrap;
   1.965 +}
   1.966 +
   1.967 +nsresult
   1.968 +AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
   1.969 +{
   1.970 +  nsresult rv = Accessible::HandleAccEvent(aEvent);
   1.971 +  NS_ENSURE_SUCCESS(rv, rv);
   1.972 +
   1.973 +    Accessible* accessible = aEvent->GetAccessible();
   1.974 +    NS_ENSURE_TRUE(accessible, NS_ERROR_FAILURE);
   1.975 +
   1.976 +    // The accessible can become defunct if we have an xpcom event listener
   1.977 +    // which decides it would be fun to change the DOM and flush layout.
   1.978 +    if (accessible->IsDefunct())
   1.979 +        return NS_OK;
   1.980 +
   1.981 +    uint32_t type = aEvent->GetEventType();
   1.982 +
   1.983 +    AtkObject* atkObj = AccessibleWrap::GetAtkObject(accessible);
   1.984 +
   1.985 +    // We don't create ATK objects for nsIAccessible plain text leaves,
   1.986 +    // just return NS_OK in such case
   1.987 +    if (!atkObj) {
   1.988 +        NS_ASSERTION(type == nsIAccessibleEvent::EVENT_SHOW ||
   1.989 +                     type == nsIAccessibleEvent::EVENT_HIDE,
   1.990 +                     "Event other than SHOW and HIDE fired for plain text leaves");
   1.991 +        return NS_OK;
   1.992 +    }
   1.993 +
   1.994 +    AccessibleWrap* accWrap = GetAccessibleWrap(atkObj);
   1.995 +    if (!accWrap) {
   1.996 +        return NS_OK; // Node is shut down
   1.997 +    }
   1.998 +
   1.999 +    switch (type) {
  1.1000 +    case nsIAccessibleEvent::EVENT_STATE_CHANGE:
  1.1001 +        return FireAtkStateChangeEvent(aEvent, atkObj);
  1.1002 +
  1.1003 +    case nsIAccessibleEvent::EVENT_TEXT_REMOVED:
  1.1004 +    case nsIAccessibleEvent::EVENT_TEXT_INSERTED:
  1.1005 +        return FireAtkTextChangedEvent(aEvent, atkObj);
  1.1006 +
  1.1007 +    case nsIAccessibleEvent::EVENT_FOCUS:
  1.1008 +      {
  1.1009 +        a11y::RootAccessible* rootAccWrap = accWrap->RootAccessible();
  1.1010 +        if (rootAccWrap && rootAccWrap->mActivated) {
  1.1011 +            atk_focus_tracker_notify(atkObj);
  1.1012 +            // Fire state change event for focus
  1.1013 +            atk_object_notify_state_change(atkObj, ATK_STATE_FOCUSED, true);
  1.1014 +            return NS_OK;
  1.1015 +        }
  1.1016 +      } break;
  1.1017 +
  1.1018 +    case nsIAccessibleEvent::EVENT_NAME_CHANGE:
  1.1019 +      {
  1.1020 +        nsAutoString newName;
  1.1021 +        accessible->Name(newName);
  1.1022 +
  1.1023 +        MaybeFireNameChange(atkObj, newName);
  1.1024 +
  1.1025 +        break;
  1.1026 +      }
  1.1027 +    case nsIAccessibleEvent::EVENT_VALUE_CHANGE:
  1.1028 +      {
  1.1029 +        nsCOMPtr<nsIAccessibleValue> value(do_QueryObject(accessible));
  1.1030 +        if (value) {    // Make sure this is a numeric value
  1.1031 +            // Don't fire for MSAA string value changes (e.g. text editing)
  1.1032 +            // ATK values are always numeric
  1.1033 +            g_object_notify( (GObject*)atkObj, "accessible-value" );
  1.1034 +        }
  1.1035 +      } break;
  1.1036 +
  1.1037 +    case nsIAccessibleEvent::EVENT_SELECTION:
  1.1038 +    case nsIAccessibleEvent::EVENT_SELECTION_ADD:
  1.1039 +    case nsIAccessibleEvent::EVENT_SELECTION_REMOVE:
  1.1040 +    {
  1.1041 +      // XXX: dupe events may be fired
  1.1042 +      AccSelChangeEvent* selChangeEvent = downcast_accEvent(aEvent);
  1.1043 +      g_signal_emit_by_name(AccessibleWrap::GetAtkObject(selChangeEvent->Widget()),
  1.1044 +                            "selection_changed");
  1.1045 +      break;
  1.1046 +    }
  1.1047 +
  1.1048 +    case nsIAccessibleEvent::EVENT_SELECTION_WITHIN:
  1.1049 +    {
  1.1050 +      g_signal_emit_by_name(atkObj, "selection_changed");
  1.1051 +      break;
  1.1052 +    }
  1.1053 +
  1.1054 +    case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED:
  1.1055 +        g_signal_emit_by_name(atkObj, "text_selection_changed");
  1.1056 +        break;
  1.1057 +
  1.1058 +    case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED:
  1.1059 +      {
  1.1060 +        AccCaretMoveEvent* caretMoveEvent = downcast_accEvent(aEvent);
  1.1061 +        NS_ASSERTION(caretMoveEvent, "Event needs event data");
  1.1062 +        if (!caretMoveEvent)
  1.1063 +            break;
  1.1064 +
  1.1065 +        int32_t caretOffset = caretMoveEvent->GetCaretOffset();
  1.1066 +        g_signal_emit_by_name(atkObj, "text_caret_moved", caretOffset);
  1.1067 +      } break;
  1.1068 +
  1.1069 +    case nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED:
  1.1070 +        g_signal_emit_by_name(atkObj, "text-attributes-changed");
  1.1071 +        break;
  1.1072 +
  1.1073 +    case nsIAccessibleEvent::EVENT_TABLE_MODEL_CHANGED:
  1.1074 +        g_signal_emit_by_name(atkObj, "model_changed");
  1.1075 +        break;
  1.1076 +
  1.1077 +    case nsIAccessibleEvent::EVENT_TABLE_ROW_INSERT:
  1.1078 +      {
  1.1079 +        AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent);
  1.1080 +        NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE);
  1.1081 +
  1.1082 +        int32_t rowIndex = tableEvent->GetIndex();
  1.1083 +        int32_t numRows = tableEvent->GetCount();
  1.1084 +
  1.1085 +        g_signal_emit_by_name(atkObj, "row_inserted", rowIndex, numRows);
  1.1086 +     } break;
  1.1087 +
  1.1088 +   case nsIAccessibleEvent::EVENT_TABLE_ROW_DELETE:
  1.1089 +     {
  1.1090 +        AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent);
  1.1091 +        NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE);
  1.1092 +
  1.1093 +        int32_t rowIndex = tableEvent->GetIndex();
  1.1094 +        int32_t numRows = tableEvent->GetCount();
  1.1095 +
  1.1096 +        g_signal_emit_by_name(atkObj, "row_deleted", rowIndex, numRows);
  1.1097 +      } break;
  1.1098 +
  1.1099 +    case nsIAccessibleEvent::EVENT_TABLE_ROW_REORDER:
  1.1100 +      {
  1.1101 +        g_signal_emit_by_name(atkObj, "row_reordered");
  1.1102 +        break;
  1.1103 +      }
  1.1104 +
  1.1105 +    case nsIAccessibleEvent::EVENT_TABLE_COLUMN_INSERT:
  1.1106 +      {
  1.1107 +        AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent);
  1.1108 +        NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE);
  1.1109 +
  1.1110 +        int32_t colIndex = tableEvent->GetIndex();
  1.1111 +        int32_t numCols = tableEvent->GetCount();
  1.1112 +        g_signal_emit_by_name(atkObj, "column_inserted", colIndex, numCols);
  1.1113 +      } break;
  1.1114 +
  1.1115 +    case nsIAccessibleEvent::EVENT_TABLE_COLUMN_DELETE:
  1.1116 +      {
  1.1117 +        AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent);
  1.1118 +        NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE);
  1.1119 +
  1.1120 +        int32_t colIndex = tableEvent->GetIndex();
  1.1121 +        int32_t numCols = tableEvent->GetCount();
  1.1122 +        g_signal_emit_by_name(atkObj, "column_deleted", colIndex, numCols);
  1.1123 +      } break;
  1.1124 +
  1.1125 +    case nsIAccessibleEvent::EVENT_TABLE_COLUMN_REORDER:
  1.1126 +        g_signal_emit_by_name(atkObj, "column_reordered");
  1.1127 +        break;
  1.1128 +
  1.1129 +    case nsIAccessibleEvent::EVENT_SECTION_CHANGED:
  1.1130 +        g_signal_emit_by_name(atkObj, "visible_data_changed");
  1.1131 +        break;
  1.1132 +
  1.1133 +    case nsIAccessibleEvent::EVENT_SHOW:
  1.1134 +        return FireAtkShowHideEvent(aEvent, atkObj, true);
  1.1135 +
  1.1136 +    case nsIAccessibleEvent::EVENT_HIDE:
  1.1137 +        // XXX - Handle native dialog accessibles.
  1.1138 +        if (!accessible->IsRoot() && accessible->HasARIARole() &&
  1.1139 +            accessible->ARIARole() == roles::DIALOG) {
  1.1140 +          guint id = g_signal_lookup("deactivate", MAI_TYPE_ATK_OBJECT);
  1.1141 +          g_signal_emit(atkObj, id, 0);
  1.1142 +        }
  1.1143 +        return FireAtkShowHideEvent(aEvent, atkObj, false);
  1.1144 +
  1.1145 +        /*
  1.1146 +         * Because dealing with menu is very different between nsIAccessible
  1.1147 +         * and ATK, and the menu activity is important, specially transfer the
  1.1148 +         * following two event.
  1.1149 +         * Need more verification by AT test.
  1.1150 +         */
  1.1151 +    case nsIAccessibleEvent::EVENT_MENU_START:
  1.1152 +    case nsIAccessibleEvent::EVENT_MENU_END:
  1.1153 +        break;
  1.1154 +
  1.1155 +    case nsIAccessibleEvent::EVENT_WINDOW_ACTIVATE:
  1.1156 +      {
  1.1157 +        accessible->AsRoot()->mActivated = true;
  1.1158 +        guint id = g_signal_lookup("activate", MAI_TYPE_ATK_OBJECT);
  1.1159 +        g_signal_emit(atkObj, id, 0);
  1.1160 +
  1.1161 +        // Always fire a current focus event after activation.
  1.1162 +        FocusMgr()->ForceFocusEvent();
  1.1163 +      } break;
  1.1164 +
  1.1165 +    case nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE:
  1.1166 +      {
  1.1167 +        accessible->AsRoot()->mActivated = false;
  1.1168 +        guint id = g_signal_lookup("deactivate", MAI_TYPE_ATK_OBJECT);
  1.1169 +        g_signal_emit(atkObj, id, 0);
  1.1170 +      } break;
  1.1171 +
  1.1172 +    case nsIAccessibleEvent::EVENT_WINDOW_MAXIMIZE:
  1.1173 +      {
  1.1174 +        guint id = g_signal_lookup("maximize", MAI_TYPE_ATK_OBJECT);
  1.1175 +        g_signal_emit(atkObj, id, 0);
  1.1176 +      } break;
  1.1177 +
  1.1178 +    case nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE:
  1.1179 +      {
  1.1180 +        guint id = g_signal_lookup("minimize", MAI_TYPE_ATK_OBJECT);
  1.1181 +        g_signal_emit(atkObj, id, 0);
  1.1182 +      } break;
  1.1183 +
  1.1184 +    case nsIAccessibleEvent::EVENT_WINDOW_RESTORE:
  1.1185 +      {
  1.1186 +        guint id = g_signal_lookup("restore", MAI_TYPE_ATK_OBJECT);
  1.1187 +        g_signal_emit(atkObj, id, 0);
  1.1188 +      } break;
  1.1189 +
  1.1190 +    case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE:
  1.1191 +        g_signal_emit_by_name (atkObj, "load_complete");
  1.1192 +        // XXX - Handle native dialog accessibles.
  1.1193 +        if (!accessible->IsRoot() && accessible->HasARIARole() &&
  1.1194 +            accessible->ARIARole() == roles::DIALOG) {
  1.1195 +          guint id = g_signal_lookup("activate", MAI_TYPE_ATK_OBJECT);
  1.1196 +          g_signal_emit(atkObj, id, 0);
  1.1197 +        }
  1.1198 +      break;
  1.1199 +
  1.1200 +    case nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD:
  1.1201 +        g_signal_emit_by_name (atkObj, "reload");
  1.1202 +      break;
  1.1203 +
  1.1204 +    case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED:
  1.1205 +        g_signal_emit_by_name (atkObj, "load_stopped");
  1.1206 +      break;
  1.1207 +
  1.1208 +    case nsIAccessibleEvent::EVENT_MENUPOPUP_START:
  1.1209 +        atk_focus_tracker_notify(atkObj); // fire extra focus event
  1.1210 +        atk_object_notify_state_change(atkObj, ATK_STATE_VISIBLE, true);
  1.1211 +        atk_object_notify_state_change(atkObj, ATK_STATE_SHOWING, true);
  1.1212 +        break;
  1.1213 +
  1.1214 +    case nsIAccessibleEvent::EVENT_MENUPOPUP_END:
  1.1215 +        atk_object_notify_state_change(atkObj, ATK_STATE_VISIBLE, false);
  1.1216 +        atk_object_notify_state_change(atkObj, ATK_STATE_SHOWING, false);
  1.1217 +        break;
  1.1218 +    }
  1.1219 +
  1.1220 +    return NS_OK;
  1.1221 +}
  1.1222 +
  1.1223 +nsresult
  1.1224 +AccessibleWrap::FireAtkStateChangeEvent(AccEvent* aEvent,
  1.1225 +                                        AtkObject* aObject)
  1.1226 +{
  1.1227 +    AccStateChangeEvent* event = downcast_accEvent(aEvent);
  1.1228 +    NS_ENSURE_TRUE(event, NS_ERROR_FAILURE);
  1.1229 +
  1.1230 +    bool isEnabled = event->IsStateEnabled();
  1.1231 +    int32_t stateIndex = AtkStateMap::GetStateIndexFor(event->GetState());
  1.1232 +    if (stateIndex >= 0) {
  1.1233 +        NS_ASSERTION(gAtkStateMap[stateIndex].stateMapEntryType != kNoSuchState,
  1.1234 +                     "No such state");
  1.1235 +
  1.1236 +        if (gAtkStateMap[stateIndex].atkState != kNone) {
  1.1237 +            NS_ASSERTION(gAtkStateMap[stateIndex].stateMapEntryType != kNoStateChange,
  1.1238 +                         "State changes should not fired for this state");
  1.1239 +
  1.1240 +            if (gAtkStateMap[stateIndex].stateMapEntryType == kMapOpposite)
  1.1241 +                isEnabled = !isEnabled;
  1.1242 +
  1.1243 +            // Fire state change for first state if there is one to map
  1.1244 +            atk_object_notify_state_change(aObject,
  1.1245 +                                           gAtkStateMap[stateIndex].atkState,
  1.1246 +                                           isEnabled);
  1.1247 +        }
  1.1248 +    }
  1.1249 +
  1.1250 +    return NS_OK;
  1.1251 +}
  1.1252 +
  1.1253 +nsresult
  1.1254 +AccessibleWrap::FireAtkTextChangedEvent(AccEvent* aEvent,
  1.1255 +                                        AtkObject* aObject)
  1.1256 +{
  1.1257 +    AccTextChangeEvent* event = downcast_accEvent(aEvent);
  1.1258 +    NS_ENSURE_TRUE(event, NS_ERROR_FAILURE);
  1.1259 +
  1.1260 +    int32_t start = event->GetStartOffset();
  1.1261 +    uint32_t length = event->GetLength();
  1.1262 +    bool isInserted = event->IsTextInserted();
  1.1263 +    bool isFromUserInput = aEvent->IsFromUserInput();
  1.1264 +    char* signal_name = nullptr;
  1.1265 +
  1.1266 +  if (gAvailableAtkSignals == eUnknown)
  1.1267 +    gAvailableAtkSignals =
  1.1268 +      g_signal_lookup("text-insert", G_OBJECT_TYPE(aObject)) ?
  1.1269 +        eHaveNewAtkTextSignals : eNoNewAtkSignals;
  1.1270 +
  1.1271 +  if (gAvailableAtkSignals == eNoNewAtkSignals) {
  1.1272 +    // XXX remove this code and the gHaveNewTextSignals check when we can
  1.1273 +    // stop supporting old atk since it doesn't really work anyway
  1.1274 +    // see bug 619002
  1.1275 +    signal_name = g_strconcat(isInserted ? "text_changed::insert" :
  1.1276 +                              "text_changed::delete",
  1.1277 +                              isFromUserInput ? "" : kNonUserInputEvent, nullptr);
  1.1278 +    g_signal_emit_by_name(aObject, signal_name, start, length);
  1.1279 +  } else {
  1.1280 +    nsAutoString text;
  1.1281 +    event->GetModifiedText(text);
  1.1282 +    signal_name = g_strconcat(isInserted ? "text-insert" : "text-remove",
  1.1283 +                              isFromUserInput ? "" : "::system", nullptr);
  1.1284 +    g_signal_emit_by_name(aObject, signal_name, start, length,
  1.1285 +                          NS_ConvertUTF16toUTF8(text).get());
  1.1286 +  }
  1.1287 +
  1.1288 +  g_free(signal_name);
  1.1289 +  return NS_OK;
  1.1290 +}
  1.1291 +
  1.1292 +nsresult
  1.1293 +AccessibleWrap::FireAtkShowHideEvent(AccEvent* aEvent,
  1.1294 +                                     AtkObject* aObject, bool aIsAdded)
  1.1295 +{
  1.1296 +    int32_t indexInParent = getIndexInParentCB(aObject);
  1.1297 +    AtkObject *parentObject = getParentCB(aObject);
  1.1298 +    NS_ENSURE_STATE(parentObject);
  1.1299 +
  1.1300 +    bool isFromUserInput = aEvent->IsFromUserInput();
  1.1301 +    char *signal_name = g_strconcat(aIsAdded ? "children_changed::add" :  "children_changed::remove",
  1.1302 +                                    isFromUserInput ? "" : kNonUserInputEvent, nullptr);
  1.1303 +    g_signal_emit_by_name(parentObject, signal_name, indexInParent, aObject, nullptr);
  1.1304 +    g_free(signal_name);
  1.1305 +
  1.1306 +    return NS_OK;
  1.1307 +}

mercurial