accessible/src/atk/AccessibleWrap.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial