ApplicationAccessibleWrap.cpp (5008B)
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 "ApplicationAccessibleWrap.h" 8 9 #include "nsMai.h" 10 #include "nsAccessibilityService.h" 11 12 #include <gtk/gtk.h> 13 #include "atk/atkobject.h" 14 15 using namespace mozilla; 16 using namespace mozilla::a11y; 17 18 // ApplicationAccessibleWrap 19 20 ApplicationAccessibleWrap::ApplicationAccessibleWrap() = default; 21 22 ApplicationAccessibleWrap::~ApplicationAccessibleWrap() { 23 AccessibleWrap::ShutdownAtkObject(); 24 } 25 26 gboolean toplevel_event_watcher(GSignalInvocationHint* ihint, 27 guint n_param_values, 28 const GValue* param_values, gpointer data) { 29 static GQuark sQuark_gecko_acc_obj = 0; 30 31 if (!sQuark_gecko_acc_obj) { 32 sQuark_gecko_acc_obj = g_quark_from_static_string("GeckoAccObj"); 33 } 34 35 if (nsAccessibilityService::IsShutdown()) return TRUE; 36 37 GObject* object = 38 reinterpret_cast<GObject*>(g_value_get_object(param_values)); 39 if (!GTK_IS_WINDOW(object)) return TRUE; 40 41 AtkObject* child = gtk_widget_get_accessible(GTK_WIDGET(object)); 42 AtkRole role = atk_object_get_role(child); 43 44 // GTK native dialog 45 if (!IS_MAI_OBJECT(child) && 46 (role == ATK_ROLE_DIALOG || role == ATK_ROLE_FILE_CHOOSER || 47 role == ATK_ROLE_COLOR_CHOOSER || role == ATK_ROLE_FONT_CHOOSER)) { 48 if (data == reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_SHOW)) { 49 // Attach the dialog accessible to app accessible tree 50 LocalAccessible* windowAcc = 51 GetAccService()->AddNativeRootAccessible(child); 52 g_object_set_qdata(G_OBJECT(child), sQuark_gecko_acc_obj, 53 reinterpret_cast<gpointer>(windowAcc)); 54 55 } else { 56 // Deattach the dialog accessible 57 LocalAccessible* windowAcc = reinterpret_cast<LocalAccessible*>( 58 g_object_get_qdata(G_OBJECT(child), sQuark_gecko_acc_obj)); 59 if (windowAcc) { 60 GetAccService()->RemoveNativeRootAccessible(windowAcc); 61 g_object_set_qdata(G_OBJECT(child), sQuark_gecko_acc_obj, nullptr); 62 } 63 } 64 } 65 66 return TRUE; 67 } 68 69 ENameValueFlag ApplicationAccessibleWrap::DirectName(nsString& aName) const { 70 // ATK doesn't provide a way to obtain an application name (for example, 71 // Firefox or Thunderbird) like IA2 does. Thus let's return an application 72 // name as accessible name that was used to get a branding name (for example, 73 // Minefield aka nightly Firefox or Daily aka nightly Thunderbird). 74 AppName(aName); 75 return eNameOK; 76 } 77 78 void ApplicationAccessibleWrap::GetNativeInterface(void** aOutAccessible) { 79 *aOutAccessible = nullptr; 80 81 if (!mAtkObject) { 82 mAtkObject = reinterpret_cast<AtkObject*>( 83 g_object_new(MAI_TYPE_ATK_OBJECT, nullptr)); 84 if (!mAtkObject) return; 85 86 atk_object_initialize(mAtkObject, static_cast<Accessible*>(this)); 87 mAtkObject->role = ATK_ROLE_INVALID; 88 mAtkObject->layer = ATK_LAYER_INVALID; 89 } 90 91 *aOutAccessible = mAtkObject; 92 } 93 94 struct AtkRootAccessibleAddedEvent { 95 AtkObject* app_accessible; 96 AtkObject* root_accessible; 97 uint32_t index; 98 }; 99 100 gboolean fireRootAccessibleAddedCB(gpointer data) { 101 AtkRootAccessibleAddedEvent* eventData = (AtkRootAccessibleAddedEvent*)data; 102 g_signal_emit_by_name(eventData->app_accessible, "children_changed::add", 103 eventData->index, eventData->root_accessible, nullptr); 104 g_object_unref(eventData->app_accessible); 105 g_object_unref(eventData->root_accessible); 106 free(data); 107 108 return FALSE; 109 } 110 111 bool ApplicationAccessibleWrap::InsertChildAt(uint32_t aIdx, 112 LocalAccessible* aChild) { 113 if (!ApplicationAccessible::InsertChildAt(aIdx, aChild)) return false; 114 115 AtkObject* atkAccessible = AccessibleWrap::GetAtkObject(aChild); 116 atk_object_set_parent(atkAccessible, mAtkObject); 117 118 uint32_t count = mChildren.Length(); 119 120 // Emit children_changed::add in a timeout 121 // to make sure aRootAccWrap is fully initialized. 122 AtkRootAccessibleAddedEvent* eventData = 123 (AtkRootAccessibleAddedEvent*)malloc(sizeof(AtkRootAccessibleAddedEvent)); 124 if (eventData) { 125 eventData->app_accessible = mAtkObject; 126 eventData->root_accessible = atkAccessible; 127 eventData->index = count - 1; 128 g_object_ref(mAtkObject); 129 g_object_ref(atkAccessible); 130 g_timeout_add(0, fireRootAccessibleAddedCB, eventData); 131 } 132 133 return true; 134 } 135 136 bool ApplicationAccessibleWrap::RemoveChild(LocalAccessible* aChild) { 137 int32_t index = aChild->IndexInParent(); 138 139 AtkObject* atkAccessible = AccessibleWrap::GetAtkObject(aChild); 140 atk_object_set_parent(atkAccessible, nullptr); 141 g_signal_emit_by_name(mAtkObject, "children_changed::remove", index, 142 atkAccessible, nullptr); 143 144 return ApplicationAccessible::RemoveChild(aChild); 145 }