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