• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 Igalia S.L.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include "config.h"
30 #include "AccessibilityCallbacks.h"
31 
32 #include "AccessibilityController.h"
33 #include "DumpRenderTree.h"
34 #include "GOwnPtr.h"
35 #include "WebCoreSupport/DumpRenderTreeSupportGtk.h"
36 #include <gtk/gtk.h>
37 #include <webkit/webkit.h>
38 
39 static guint stateChangeListenerId = 0;
40 static guint focusEventListenerId = 0;
41 static guint activeDescendantChangedListenerId = 0;
42 static guint childrenChangedListenerId = 0;
43 static guint propertyChangedListenerId = 0;
44 static guint visibleDataChangedListenerId = 0;
45 
46 static guint loadCompleteListenerId = 0;
47 static guint loadStoppedListenerId = 0;
48 static guint reloadListenerId = 0;
49 
printAccessibilityEvent(AtkObject * accessible,const gchar * signalName)50 static void printAccessibilityEvent(AtkObject* accessible, const gchar* signalName)
51 {
52     // Sanity check.
53     if (!accessible || !ATK_IS_OBJECT(accessible) || !signalName)
54         return;
55 
56     const gchar* objectName = atk_object_get_name(accessible);
57     guint objectRole = atk_object_get_role(accessible);
58 
59     // Try to always provide a name to be logged for the object.
60     if (!objectName || *objectName == '\0')
61         objectName = "(No name)";
62 
63     printf("Accessibility object emitted \"%s\" / Name: \"%s\" / Role: %d\n",
64            signalName, objectName, objectRole);
65 }
66 
axObjectEventListener(GSignalInvocationHint * signalHint,guint numParamValues,const GValue * paramValues,gpointer data)67 static gboolean axObjectEventListener(GSignalInvocationHint *signalHint,
68                                       guint numParamValues,
69                                       const GValue *paramValues,
70                                       gpointer data)
71 {
72     // At least we should receive the instance emitting the signal.
73     if (numParamValues < 1)
74         return TRUE;
75 
76     AtkObject* accessible = ATK_OBJECT(g_value_get_object(&paramValues[0]));
77     if (!accessible || !ATK_IS_OBJECT(accessible))
78         return TRUE;
79 
80     GSignalQuery signal_query;
81     GOwnPtr<gchar> signalName;
82 
83     g_signal_query(signalHint->signal_id, &signal_query);
84 
85     if (!g_strcmp0(signal_query.signal_name, "state-change")) {
86         signalName.set(g_strdup_printf("state-change:%s = %d",
87                                        g_value_get_string(&paramValues[1]),
88                                        g_value_get_boolean(&paramValues[2])));
89     } else if (!g_strcmp0(signal_query.signal_name, "focus-event")) {
90         signalName.set(g_strdup_printf("focus-event = %d",
91                                        g_value_get_boolean(&paramValues[1])));
92     } else if (!g_strcmp0(signal_query.signal_name, "children-changed")) {
93         signalName.set(g_strdup_printf("children-changed = %d",
94                                        g_value_get_uint(&paramValues[1])));
95     } else if (!g_strcmp0(signal_query.signal_name, "property-change")) {
96         signalName.set(g_strdup_printf("property-change:%s",
97                                        g_quark_to_string(signalHint->detail)));
98     } else
99         signalName.set(g_strdup(signal_query.signal_name));
100 
101     printAccessibilityEvent(accessible, signalName.get());
102 
103     return TRUE;
104 }
105 
axDocumentEventListener(GSignalInvocationHint * signalHint,guint numParamValues,const GValue * paramValues,gpointer data)106 static gboolean axDocumentEventListener(GSignalInvocationHint *signalHint,
107                                         guint numParamValues,
108                                         const GValue *paramValues,
109                                         gpointer data)
110 {
111     // At least we should receive the instance emitting the signal.
112     if (numParamValues < 1)
113         return TRUE;
114 
115     AtkObject* accessible = ATK_OBJECT(g_value_get_object(&paramValues[0]));
116     if (!accessible || !ATK_IS_DOCUMENT(accessible))
117         return TRUE;
118 
119     GSignalQuery signal_query;
120     g_signal_query(signalHint->signal_id, &signal_query);
121 
122     printAccessibilityEvent(accessible, signal_query.signal_name);
123 
124     return TRUE;
125 }
126 
connectAccessibilityCallbacks()127 void connectAccessibilityCallbacks()
128 {
129     // Ensure no callbacks are connected before.
130     disconnectAccessibilityCallbacks();
131 
132     // Ensure that accessibility is initialized for the WebView by querying for
133     // the root accessible object, which will create the full hierarchy.
134     DumpRenderTreeSupportGtk::getRootAccessibleElement(mainFrame);
135 
136     // Add global listeners for AtkObject's signals.
137     stateChangeListenerId = atk_add_global_event_listener(axObjectEventListener, "Gtk:AtkObject:state-change");
138     focusEventListenerId = atk_add_global_event_listener(axObjectEventListener, "Gtk:AtkObject:focus-event");
139     activeDescendantChangedListenerId = atk_add_global_event_listener(axObjectEventListener, "Gtk:AtkObject:active-descendant-changed");
140     childrenChangedListenerId = atk_add_global_event_listener(axObjectEventListener, "Gtk:AtkObject:children-changed");
141     propertyChangedListenerId = atk_add_global_event_listener(axObjectEventListener, "Gtk:AtkObject:property-change");
142     visibleDataChangedListenerId = atk_add_global_event_listener(axObjectEventListener, "Gtk:AtkObject:visible-data-changed");
143 
144     // Ensure the Atk interface types are registered, otherwise
145     // the AtkDocument signal handlers below won't get registered.
146     GObject* dummyAxObject = G_OBJECT(g_object_new(ATK_TYPE_OBJECT, 0));
147     AtkObject* dummyNoOpAxObject = atk_no_op_object_new(dummyAxObject);
148     g_object_unref(G_OBJECT(dummyNoOpAxObject));
149     g_object_unref(dummyAxObject);
150 
151     // Add global listeners for AtkDocument's signals.
152     loadCompleteListenerId = atk_add_global_event_listener(axDocumentEventListener, "Gtk:AtkDocument:load-complete");
153     loadStoppedListenerId = atk_add_global_event_listener(axDocumentEventListener, "Gtk:AtkDocument:load-stopped");
154     reloadListenerId = atk_add_global_event_listener(axDocumentEventListener, "Gtk:AtkDocument:reload");
155 }
156 
disconnectAccessibilityCallbacks()157 void disconnectAccessibilityCallbacks()
158 {
159     // AtkObject signals.
160     if (stateChangeListenerId) {
161         atk_remove_global_event_listener(stateChangeListenerId);
162         stateChangeListenerId = 0;
163     }
164     if (focusEventListenerId) {
165         atk_remove_global_event_listener(focusEventListenerId);
166         focusEventListenerId = 0;
167     }
168     if (activeDescendantChangedListenerId) {
169         atk_remove_global_event_listener(activeDescendantChangedListenerId);
170         activeDescendantChangedListenerId = 0;
171     }
172     if (childrenChangedListenerId) {
173         atk_remove_global_event_listener(childrenChangedListenerId);
174         childrenChangedListenerId = 0;
175     }
176     if (propertyChangedListenerId) {
177         atk_remove_global_event_listener(propertyChangedListenerId);
178         propertyChangedListenerId = 0;
179     }
180     if (visibleDataChangedListenerId) {
181         atk_remove_global_event_listener(visibleDataChangedListenerId);
182         visibleDataChangedListenerId = 0;
183     }
184 
185     // AtkDocument signals.
186     if (loadCompleteListenerId) {
187         atk_remove_global_event_listener(loadCompleteListenerId);
188         loadCompleteListenerId = 0;
189     }
190     if (loadStoppedListenerId) {
191         atk_remove_global_event_listener(loadStoppedListenerId);
192         loadStoppedListenerId = 0;
193     }
194     if (reloadListenerId) {
195         atk_remove_global_event_listener(reloadListenerId);
196         reloadListenerId = 0;
197     }
198 }
199 
200