Home | History | Annotate | Download | only in gtk
      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 
     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 
     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 
    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 
    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 
    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