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