[ATK] Missing WTR AccessibilityUIElement::addNotificationListener implementation
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 20 Sep 2013 15:15:22 +0000 (15:15 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 20 Sep 2013 15:15:22 +0000 (15:15 +0000)
https://bugs.webkit.org/show_bug.cgi?id=120421

Patch by Denis Nomiyama <d.nomiyama@samsung.com> on 2013-09-20
Reviewed by Mario Sanchez Prada.

Implemented the notification listener for AccessibilityUIElement for
WebKitTestRunner. The signal is generated by
AXObjectCache::postPlatformNotification() and received by
axObjectEventListener(). axObjectEventListener() will then invoke
JSObjectCallAsFunction() with the respective callback function. The
notification handlers are stored in HashMap in
AccessibilityNotificationHandlerAtk.cpp.

Moved ATK signal handling (printAccessibilityEvent and
axObjectEventListener) to AccessibilityNotificationHandlerAtk.cpp.

* WebKitTestRunner/CMakeLists.txt: Added InjectedBundle/atk to the
include path.
* WebKitTestRunner/GNUmakefile.am:
Added AccessibilityNotificationHandlerAtk.cpp/h.
* WebKitTestRunner/InjectedBundle/AccessibilityController.cpp: Moved
listener IDs to AccessibilityNotificationHandlerAtk.cpp.
* WebKitTestRunner/InjectedBundle/AccessibilityController.h: Moved
listener IDs to AccessibilityNotificationHandlerAtk.cpp.
* WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h: Added a
notification handler for GTK+ and EFL.
* WebKitTestRunner/InjectedBundle/atk/AccessibilityControllerAtk.cpp:
Moved printAccessibilityEvent and axObjectEventListener to
AccessibilityNotificationHandlerAtk.cpp.
(WTR::AccessibilityController::logAccessibilityEvents): Moved listener
IDs to AccessibilityNotificationHandlerAtk.cpp.
(WTR::AccessibilityController::resetToConsistentState): Moved listener
IDs to AccessibilityNotificationHandlerAtk.cpp.
* WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.cpp: Added.
(WTR::printAccessibilityEvent): Prints accessibility events. This
function was moved from AccessibilityControllerAtk.cpp.
(WTR::axObjectEventListener): Callback for signals. This function was
moved from AccessibilityControllerAtk.cpp.
(WTR::AccessibilityNotificationHandler::AccessibilityNotificationHandler):
Initialize element and function callback.
(WTR::AccessibilityNotificationHandler::~AccessibilityNotificationHandler):
Removes handler from HashMap and disconnects callbacks if necessary.
(WTR::AccessibilityNotificationHandler::logAccessibilityEvents): Sets
logging mode and connects callbacks.
(WTR::AccessibilityNotificationHandler::setNotificationFunctionCallback):
Sets the notification callback and connects callback to signals.
(WTR::AccessibilityNotificationHandler::removeAccessibilityNotificationHandler):
Removes the notification callback from HashMap.
(WTR::AccessibilityNotificationHandler::connectAccessibilityCallbacks):
Connects axObjectEventListener to ATK signals.
(WTR::AccessibilityNotificationHandler::disconnectAccessibilityCallbacks):
Disconnects callback.
* WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.h: Added.
(WTR::AccessibilityNotificationHandler::create): Creates a new instance
of AccessibilityNotificationHandler.
(WTR::AccessibilityNotificationHandler::setPlatformElement): Sets the
element.
(WTR::AccessibilityNotificationHandler::platformElement): Gets the
element.
(WTR::AccessibilityNotificationHandler::notificationFunctionCallback):
Gets the notification callback.
* WebKitTestRunner/InjectedBundle/atk/AccessibilityUIElementAtk.cpp:
(WTR::AccessibilityUIElement::addNotificationListener): Creates
notification handler, set the platform element and the notification
callback.
(WTR::AccessibilityUIElement::removeNotificationListener): Deletes the
notification handler.
* WebKitTestRunner/PlatformEfl.cmake:
Added AccessibilityNotificationHandlerAtk.cpp.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@156164 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Tools/ChangeLog
Tools/WebKitTestRunner/CMakeLists.txt
Tools/WebKitTestRunner/GNUmakefile.am
Tools/WebKitTestRunner/InjectedBundle/AccessibilityController.cpp
Tools/WebKitTestRunner/InjectedBundle/AccessibilityController.h
Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h
Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityControllerAtk.cpp
Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.cpp [new file with mode: 0644]
Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.h [new file with mode: 0644]
Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityUIElementAtk.cpp
Tools/WebKitTestRunner/PlatformEfl.cmake

index 5778333eb3f94dd006b5f4e72fa6781be8f7ea26..b18d632d44fdec64f80839a5a588b1fb2ace9ca5 100644 (file)
@@ -1,3 +1,75 @@
+2013-09-20  Denis Nomiyama  <d.nomiyama@samsung.com>
+
+        [ATK] Missing WTR AccessibilityUIElement::addNotificationListener implementation
+        https://bugs.webkit.org/show_bug.cgi?id=120421
+
+        Reviewed by Mario Sanchez Prada.
+
+        Implemented the notification listener for AccessibilityUIElement for
+        WebKitTestRunner. The signal is generated by
+        AXObjectCache::postPlatformNotification() and received by
+        axObjectEventListener(). axObjectEventListener() will then invoke
+        JSObjectCallAsFunction() with the respective callback function. The
+        notification handlers are stored in HashMap in
+        AccessibilityNotificationHandlerAtk.cpp.
+
+        Moved ATK signal handling (printAccessibilityEvent and
+        axObjectEventListener) to AccessibilityNotificationHandlerAtk.cpp.
+
+        * WebKitTestRunner/CMakeLists.txt: Added InjectedBundle/atk to the
+        include path.
+        * WebKitTestRunner/GNUmakefile.am:
+        Added AccessibilityNotificationHandlerAtk.cpp/h.
+        * WebKitTestRunner/InjectedBundle/AccessibilityController.cpp: Moved
+        listener IDs to AccessibilityNotificationHandlerAtk.cpp.
+        * WebKitTestRunner/InjectedBundle/AccessibilityController.h: Moved
+        listener IDs to AccessibilityNotificationHandlerAtk.cpp.
+        * WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h: Added a
+        notification handler for GTK+ and EFL.
+        * WebKitTestRunner/InjectedBundle/atk/AccessibilityControllerAtk.cpp:
+        Moved printAccessibilityEvent and axObjectEventListener to
+        AccessibilityNotificationHandlerAtk.cpp.
+        (WTR::AccessibilityController::logAccessibilityEvents): Moved listener
+        IDs to AccessibilityNotificationHandlerAtk.cpp.
+        (WTR::AccessibilityController::resetToConsistentState): Moved listener
+        IDs to AccessibilityNotificationHandlerAtk.cpp.
+        * WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.cpp: Added.
+        (WTR::printAccessibilityEvent): Prints accessibility events. This
+        function was moved from AccessibilityControllerAtk.cpp.
+        (WTR::axObjectEventListener): Callback for signals. This function was
+        moved from AccessibilityControllerAtk.cpp.
+        (WTR::AccessibilityNotificationHandler::AccessibilityNotificationHandler):
+        Initialize element and function callback.
+        (WTR::AccessibilityNotificationHandler::~AccessibilityNotificationHandler):
+        Removes handler from HashMap and disconnects callbacks if necessary.
+        (WTR::AccessibilityNotificationHandler::logAccessibilityEvents): Sets
+        logging mode and connects callbacks.
+        (WTR::AccessibilityNotificationHandler::setNotificationFunctionCallback):
+        Sets the notification callback and connects callback to signals.
+        (WTR::AccessibilityNotificationHandler::removeAccessibilityNotificationHandler):
+        Removes the notification callback from HashMap.
+        (WTR::AccessibilityNotificationHandler::connectAccessibilityCallbacks):
+        Connects axObjectEventListener to ATK signals.
+        (WTR::AccessibilityNotificationHandler::disconnectAccessibilityCallbacks):
+        Disconnects callback.
+        * WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.h: Added.
+        (WTR::AccessibilityNotificationHandler::create): Creates a new instance
+        of AccessibilityNotificationHandler.
+        (WTR::AccessibilityNotificationHandler::setPlatformElement): Sets the
+        element.
+        (WTR::AccessibilityNotificationHandler::platformElement): Gets the
+        element.
+        (WTR::AccessibilityNotificationHandler::notificationFunctionCallback):
+        Gets the notification callback.
+        * WebKitTestRunner/InjectedBundle/atk/AccessibilityUIElementAtk.cpp:
+        (WTR::AccessibilityUIElement::addNotificationListener): Creates
+        notification handler, set the platform element and the notification
+        callback.
+        (WTR::AccessibilityUIElement::removeNotificationListener): Deletes the
+        notification handler.
+        * WebKitTestRunner/PlatformEfl.cmake:
+        Added AccessibilityNotificationHandlerAtk.cpp.
+
 2013-09-20  Allan Sandfeld Jensen  <allan.jensen@digia.com>
 
         Update features.pri
index 07b647b80c2488b12942710ab588fc3f20e234db..550f8dfeaf93c53ded687f7c5c220e86d86f5563 100644 (file)
@@ -24,6 +24,7 @@ set(WebKitTestRunner_INCLUDE_DIRECTORIES
     ${WEBKIT_TESTRUNNER_DIR}
     ${WEBKIT_TESTRUNNER_DIR}/InjectedBundle
     ${WEBKIT_TESTRUNNER_DIR}/InjectedBundle/Bindings
+    ${WEBKIT_TESTRUNNER_DIR}/InjectedBundle/atk
     ${JAVASCRIPTCORE_DIR}
     ${JAVASCRIPTCORE_DIR}/ForwardingHeaders
     ${WEBCORE_DIR}/editing
index bd04f4d425997b31bdb074070578020d2718ce15..b004358fbdfa697576b5e1b16c4b55e6c9d9ef5e 100644 (file)
@@ -93,6 +93,8 @@ Libraries_libTestRunnerInjectedBundle_la_SOURCES = \
        Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h \
        Tools/WebKitTestRunner/InjectedBundle/ActivateFonts.h \
        Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityControllerAtk.cpp \
+       Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.cpp \
+       Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.h \
        Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityUIElementAtk.cpp \
        Tools/WebKitTestRunner/InjectedBundle/Bindings/JSWrappable.h \
        Tools/WebKitTestRunner/InjectedBundle/Bindings/JSWrapper.cpp \
@@ -132,6 +134,7 @@ Libraries_libTestRunnerInjectedBundle_la_CPPFLAGS = \
        -I$(srcdir)/Tools/WebKitTestRunner \
        -I$(srcdir)/Tools/WebKitTestRunner/InjectedBundle \
        -I$(srcdir)/Tools/WebKitTestRunner/InjectedBundle/Bindings \
+       -I$(srcdir)/Tools/WebKitTestRunner/InjectedBundle/atk \
        -I$(srcdir)/Source/WebCore/testing/js \
        -I$(top_builddir)/DerivedSources/InjectedBundle \
        -I$(top_builddir)/DerivedSources/WebKit2/include \
index 212e9911113c079ca97bc722827bfb85d3411d58..db8aac21906626603c09aa2b204f6267cd3dfffe 100644 (file)
@@ -44,14 +44,6 @@ PassRefPtr<AccessibilityController> AccessibilityController::create()
 }
 
 AccessibilityController::AccessibilityController()
-#if PLATFORM(GTK) || PLATFORM(EFL)
-    : m_stateChangeListenerId(0)
-    , m_focusEventListenerId(0)
-    , m_activeDescendantChangedListenerId(0)
-    , m_childrenChangedListenerId(0)
-    , m_propertyChangedListenerId(0)
-    , m_visibleDataChangedListenerId(0)
-#endif
 {
 }
 
index 247de342061eaabef3807b25d02667872992abb3..330287d1ad149b99d2a52c05118cd144b73f7c23 100644 (file)
@@ -33,6 +33,9 @@
 #if PLATFORM(WIN)
 #include <windows.h>
 #endif
+#if PLATFORM(GTK) || (PLATFORM(EFL) && HAVE(ACCESSIBILITY))
+#include "AccessibilityNotificationHandlerAtk.h"
+#endif
 
 namespace WTR {
     
@@ -68,13 +71,8 @@ private:
     RetainPtr<NotificationHandler> m_globalNotificationHandler;
 #endif
 
-#if PLATFORM(GTK) || PLATFORM(EFL)
-    unsigned m_stateChangeListenerId;
-    unsigned m_focusEventListenerId;
-    unsigned m_activeDescendantChangedListenerId;
-    unsigned m_childrenChangedListenerId;
-    unsigned m_propertyChangedListenerId;
-    unsigned m_visibleDataChangedListenerId;
+#if PLATFORM(GTK) || (PLATFORM(EFL) && HAVE(ACCESSIBILITY))
+    RefPtr<AccessibilityNotificationHandler> m_globalNotificationHandler;
 #endif
 };
 
index bb1bde5d35abfe7cf8317733f256bf4186a1559a..93f7d092fc6b89ad84b2945e12f131bb7c47ebb6 100644 (file)
@@ -50,6 +50,7 @@ typedef struct objc_object* PlatformUIElement;
 
 typedef COMPtr<IAccessible> PlatformUIElement;
 #elif PLATFORM(GTK) || (PLATFORM(EFL) && HAVE(ACCESSIBILITY))
+#include "AccessibilityNotificationHandlerAtk.h"
 #include <atk/atk.h>
 #include <wtf/gobject/GRefPtr.h>
 typedef GRefPtr<AtkObject> PlatformUIElement;
@@ -262,6 +263,10 @@ private:
     void getChildren(Vector<RefPtr<AccessibilityUIElement> >&);
     void getChildrenWithRange(Vector<RefPtr<AccessibilityUIElement> >&, unsigned location, unsigned length);
 #endif
+
+#if PLATFORM(GTK) || (PLATFORM(EFL) && HAVE(ACCESSIBILITY))
+    RefPtr<AccessibilityNotificationHandler> m_notificationHandler;
+#endif
 };
     
 } // namespace WTR
index 3b58c0da0338b45665ea957b4c6bf9d8f0a24044..c1875154edc2a6178f461217080fa35b18d30807 100644 (file)
 
 namespace WTR {
 
-static void printAccessibilityEvent(AtkObject* accessible, const gchar* signalName, const gchar* signalValue)
-{
-    // Do not handle state-change:defunct signals, as the AtkObject
-    // associated to them will not be valid at this point already.
-    if (!signalName || !g_strcmp0(signalName, "state-change:defunct"))
-        return;
-
-    if (!accessible || !ATK_IS_OBJECT(accessible))
-        return;
-
-    const gchar* objectName = atk_object_get_name(accessible);
-    AtkRole objectRole = atk_object_get_role(accessible);
-
-    // Try to always provide a name to be logged for the object.
-    if (!objectName || *objectName == '\0')
-        objectName = "(No name)";
-
-    GOwnPtr<gchar> signalNameAndValue(signalValue ? g_strdup_printf("%s = %s", signalName, signalValue) : g_strdup(signalName));
-    GOwnPtr<gchar> accessibilityEventString(g_strdup_printf("Accessibility object emitted \"%s\" / Name: \"%s\" / Role: %d\n", signalNameAndValue.get(), objectName, objectRole));
-    InjectedBundle::shared().outputText(String::fromUTF8(accessibilityEventString.get()));
-}
-
-static gboolean axObjectEventListener(GSignalInvocationHint *signalHint, guint numParamValues, const GValue *paramValues, gpointer data)
-{
-    // At least we should receive the instance emitting the signal.
-    if (numParamValues < 1)
-        return TRUE;
-
-    AtkObject* accessible = ATK_OBJECT(g_value_get_object(&paramValues[0]));
-    if (!accessible || !ATK_IS_OBJECT(accessible))
-        return TRUE;
-
-    GSignalQuery signalQuery;
-    GOwnPtr<gchar> signalName;
-    GOwnPtr<gchar> signalValue;
-
-    g_signal_query(signalHint->signal_id, &signalQuery);
-
-    if (!g_strcmp0(signalQuery.signal_name, "state-change")) {
-        signalName.set(g_strdup_printf("state-change:%s", g_value_get_string(&paramValues[1])));
-        signalValue.set(g_strdup_printf("%d", g_value_get_boolean(&paramValues[2])));
-    } else if (!g_strcmp0(signalQuery.signal_name, "focus-event")) {
-        signalName.set(g_strdup("focus-event"));
-        signalValue.set(g_strdup_printf("%d", g_value_get_boolean(&paramValues[1])));
-    } else if (!g_strcmp0(signalQuery.signal_name, "children-changed")) {
-        signalName.set(g_strdup("children-changed"));
-        signalValue.set(g_strdup_printf("%d", g_value_get_uint(&paramValues[1])));
-    } else if (!g_strcmp0(signalQuery.signal_name, "property-change"))
-        signalName.set(g_strdup_printf("property-change:%s", g_quark_to_string(signalHint->detail)));
-    else
-        signalName.set(g_strdup(signalQuery.signal_name));
-
-    printAccessibilityEvent(accessible, signalName.get(), signalValue.get());
-
-    return TRUE;
-}
-
 void AccessibilityController::logAccessibilityEvents()
 {
     // Ensure no callbacks are connected before.
@@ -105,13 +48,9 @@ void AccessibilityController::logAccessibilityEvents()
     // the root accessible object, which will create the full hierarchy.
     rootElement();
 
-    // Add global listeners for AtkObject's signals.
-    m_stateChangeListenerId = atk_add_global_event_listener(axObjectEventListener, "ATK:AtkObject:state-change");
-    m_focusEventListenerId = atk_add_global_event_listener(axObjectEventListener, "ATK:AtkObject:focus-event");
-    m_activeDescendantChangedListenerId = atk_add_global_event_listener(axObjectEventListener, "ATK:AtkObject:active-descendant-changed");
-    m_childrenChangedListenerId = atk_add_global_event_listener(axObjectEventListener, "ATK:AtkObject:children-changed");
-    m_propertyChangedListenerId = atk_add_global_event_listener(axObjectEventListener, "ATK:AtkObject:property-change");
-    m_visibleDataChangedListenerId = atk_add_global_event_listener(axObjectEventListener, "ATK:AtkObject:visible-data-changed");
+    if (!m_globalNotificationHandler)
+        m_globalNotificationHandler = AccessibilityNotificationHandler::create();
+    m_globalNotificationHandler->logAccessibilityEvents();
 
     // Ensure the Atk interface types are registered, otherwise
     // the AtkDocument signal handlers below won't get registered.
@@ -123,31 +62,7 @@ void AccessibilityController::logAccessibilityEvents()
 
 void AccessibilityController::resetToConsistentState()
 {
-    // AtkObject signals.
-    if (m_stateChangeListenerId) {
-        atk_remove_global_event_listener(m_stateChangeListenerId);
-        m_stateChangeListenerId = 0;
-    }
-    if (m_focusEventListenerId) {
-        atk_remove_global_event_listener(m_focusEventListenerId);
-        m_focusEventListenerId = 0;
-    }
-    if (m_activeDescendantChangedListenerId) {
-        atk_remove_global_event_listener(m_activeDescendantChangedListenerId);
-        m_activeDescendantChangedListenerId = 0;
-    }
-    if (m_childrenChangedListenerId) {
-        atk_remove_global_event_listener(m_childrenChangedListenerId);
-        m_childrenChangedListenerId = 0;
-    }
-    if (m_propertyChangedListenerId) {
-        atk_remove_global_event_listener(m_propertyChangedListenerId);
-        m_propertyChangedListenerId = 0;
-    }
-    if (m_visibleDataChangedListenerId) {
-        atk_remove_global_event_listener(m_visibleDataChangedListenerId);
-        m_visibleDataChangedListenerId = 0;
-    }
+    m_globalNotificationHandler = 0;
 }
 
 static AtkObject* childElementById(AtkObject* parent, const char* id)
diff --git a/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.cpp b/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.cpp
new file mode 100644 (file)
index 0000000..4d154ef
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2013 Samsung Electronics Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "AccessibilityNotificationHandlerAtk.h"
+
+#include "InjectedBundle.h"
+#include "InjectedBundlePage.h"
+#include "JSWrapper.h"
+#include <WebKit2/WKBundleFrame.h>
+#include <WebKit2/WKBundlePage.h>
+#include <WebKit2/WKBundlePagePrivate.h>
+#include <wtf/HashMap.h>
+#include <wtf/gobject/GOwnPtr.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/WTFString.h>
+
+namespace WTR {
+
+namespace {
+
+typedef HashMap<AtkObject*, AccessibilityNotificationHandler*> NotificationHandlersMap;
+
+unsigned stateChangeListenerId = 0;
+unsigned focusEventListenerId = 0;
+unsigned activeDescendantChangedListenerId = 0;
+unsigned childrenChangedListenerId = 0;
+unsigned propertyChangedListenerId = 0;
+unsigned visibleDataChangedListenerId = 0;
+NotificationHandlersMap notificationHandlers;
+AccessibilityNotificationHandler* globalNotificationHandler = 0;
+bool loggingAccessibilityEvents = false;
+
+void printAccessibilityEvent(AtkObject* accessible, const char* signalName, const char* signalValue)
+{
+    // Do not handle state-change:defunct signals, as the AtkObject
+    // associated to them will not be valid at this point already.
+    if (!signalName || !g_strcmp0(signalName, "state-change:defunct"))
+        return;
+
+    if (!accessible || !ATK_IS_OBJECT(accessible))
+        return;
+
+    const char* objectName = atk_object_get_name(accessible);
+    AtkRole objectRole = atk_object_get_role(accessible);
+
+    // Try to always provide a name to be logged for the object.
+    if (!objectName || *objectName == '\0')
+        objectName = "(No name)";
+
+    GOwnPtr<char> signalNameAndValue(signalValue ? g_strdup_printf("%s = %s", signalName, signalValue) : g_strdup(signalName));
+    GOwnPtr<char> accessibilityEventString(g_strdup_printf("Accessibility object emitted \"%s\" / Name: \"%s\" / Role: %d\n", signalNameAndValue.get(), objectName, objectRole));
+    InjectedBundle::shared().outputText(String::fromUTF8(accessibilityEventString.get()));
+}
+
+gboolean axObjectEventListener(GSignalInvocationHint* signalHint, unsigned numParamValues, const GValue* paramValues, gpointer data)
+{
+    // At least we should receive the instance emitting the signal.
+    if (!numParamValues)
+        return true;
+
+    AtkObject* accessible = ATK_OBJECT(g_value_get_object(&paramValues[0]));
+    if (!accessible || !ATK_IS_OBJECT(accessible))
+        return true;
+
+    GSignalQuery signalQuery;
+    GOwnPtr<char> signalName;
+    GOwnPtr<char> signalValue;
+    const char* notificationName = 0;
+
+    g_signal_query(signalHint->signal_id, &signalQuery);
+
+    if (!g_strcmp0(signalQuery.signal_name, "state-change")) {
+        signalName.set(g_strdup_printf("state-change:%s", g_value_get_string(&paramValues[1])));
+        signalValue.set(g_strdup_printf("%d", g_value_get_boolean(&paramValues[2])));
+        if (!g_strcmp0(g_value_get_string(&paramValues[1]), "checked"))
+            notificationName = "CheckedStateChanged";
+        else if (!g_strcmp0(g_value_get_string(&paramValues[1]), "invalid-entry"))
+            notificationName = "AXInvalidStatusChanged";
+        else if (!g_strcmp0(g_value_get_string(&paramValues[1]), "layout-complete"))
+            notificationName = "AXLayoutComplete";
+    } else if (!g_strcmp0(signalQuery.signal_name, "focus-event")) {
+        signalName.set(g_strdup("focus-event"));
+        signalValue.set(g_strdup_printf("%d", g_value_get_boolean(&paramValues[1])));
+        if (g_value_get_boolean(&paramValues[1]))
+            notificationName = "AXFocusedUIElementChanged";
+    } else if (!g_strcmp0(signalQuery.signal_name, "children-changed")) {
+        signalName.set(g_strdup("children-changed"));
+        signalValue.set(g_strdup_printf("%d", g_value_get_uint(&paramValues[1])));
+    } else if (!g_strcmp0(signalQuery.signal_name, "property-change")) {
+        signalName.set(g_strdup_printf("property-change:%s", g_quark_to_string(signalHint->detail)));
+        if (!g_strcmp0(g_quark_to_string(signalHint->detail), "accessible-value"))
+            notificationName = "AXValueChanged";
+    } else
+        signalName.set(g_strdup(signalQuery.signal_name));
+
+    if (loggingAccessibilityEvents)
+        printAccessibilityEvent(accessible, signalName.get(), signalValue.get());
+
+#if PLATFORM(GTK)
+    WKBundlePageRef page = InjectedBundle::shared().page()->page();
+    WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(page);
+    JSContextRef jsContext = WKBundleFrameGetJavaScriptContext(mainFrame);
+#else
+    JSContextRef jsContext = 0;
+#endif
+    if (!jsContext)
+        return true;
+
+    if (notificationName) {
+        JSRetainPtr<JSStringRef> jsNotificationEventName(Adopt, JSStringCreateWithUTF8CString(notificationName));
+        JSValueRef notificationNameArgument = JSValueMakeString(jsContext, jsNotificationEventName.get());
+        NotificationHandlersMap::iterator elementNotificationHandler = notificationHandlers.find(accessible);
+        if (elementNotificationHandler != notificationHandlers.end()) {
+            // Listener for one element just gets one argument, the notification name.
+            JSObjectCallAsFunction(jsContext, const_cast<JSObjectRef>(elementNotificationHandler->value->notificationFunctionCallback()), 0, 1, &notificationNameArgument, 0);
+        }
+
+        if (globalNotificationHandler) {
+            // A global listener gets the element and the notification name as arguments.
+            JSValueRef arguments[2];
+            arguments[0] = toJS(jsContext, WTF::getPtr(WTR::AccessibilityUIElement::create(accessible)));
+            arguments[1] = notificationNameArgument;
+            JSObjectCallAsFunction(jsContext, const_cast<JSObjectRef>(globalNotificationHandler->notificationFunctionCallback()), 0, 2, arguments, 0);
+        }
+    }
+
+    return true;
+}
+
+} // namespace
+
+AccessibilityNotificationHandler::AccessibilityNotificationHandler()
+    : m_platformElement(0)
+    , m_notificationFunctionCallback(0)
+{
+}
+
+AccessibilityNotificationHandler::~AccessibilityNotificationHandler()
+{
+    removeAccessibilityNotificationHandler();
+    disconnectAccessibilityCallbacks();
+}
+
+void AccessibilityNotificationHandler::logAccessibilityEvents()
+{
+    connectAccessibilityCallbacks();
+    loggingAccessibilityEvents = true;
+}
+
+void AccessibilityNotificationHandler::setNotificationFunctionCallback(JSValueRef notificationFunctionCallback)
+{
+    if (!notificationFunctionCallback) {
+        removeAccessibilityNotificationHandler();
+        disconnectAccessibilityCallbacks();
+        return;
+    }
+
+    m_notificationFunctionCallback = notificationFunctionCallback;
+
+#if PLATFORM(GTK)
+    WKBundlePageRef page = InjectedBundle::shared().page()->page();
+    WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(page);
+    JSContextRef jsContext = WKBundleFrameGetJavaScriptContext(mainFrame);
+#else
+    JSContextRef jsContext = 0;
+#endif
+    if (!jsContext)
+        return;
+
+    connectAccessibilityCallbacks();
+
+    JSValueProtect(jsContext, m_notificationFunctionCallback);
+    // Check if this notification handler is related to a specific element.
+    if (m_platformElement) {
+        NotificationHandlersMap::iterator currentNotificationHandler = notificationHandlers.find(m_platformElement.get());
+        if (currentNotificationHandler != notificationHandlers.end()) {
+            ASSERT(currentNotificationHandler->value->platformElement());
+            JSValueUnprotect(jsContext, currentNotificationHandler->value->notificationFunctionCallback());
+            notificationHandlers.remove(currentNotificationHandler->value->platformElement().get());
+        }
+        notificationHandlers.add(m_platformElement.get(), this);
+    } else {
+        if (globalNotificationHandler)
+            JSValueUnprotect(jsContext, globalNotificationHandler->notificationFunctionCallback());
+        globalNotificationHandler = this;
+    }
+}
+
+void AccessibilityNotificationHandler::removeAccessibilityNotificationHandler()
+{
+#if PLATFORM(GTK)
+    WKBundlePageRef page = InjectedBundle::shared().page()->page();
+    WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(page);
+    JSContextRef jsContext = WKBundleFrameGetJavaScriptContext(mainFrame);
+#else
+    JSContextRef jsContext = 0;
+#endif
+    if (!jsContext)
+        return;
+
+    if (globalNotificationHandler == this) {
+        JSValueUnprotect(jsContext, globalNotificationHandler->notificationFunctionCallback());
+        globalNotificationHandler = 0;
+    } else if (m_platformElement.get()) {
+        NotificationHandlersMap::iterator removeNotificationHandler = notificationHandlers.find(m_platformElement.get());
+        if (removeNotificationHandler != notificationHandlers.end()) {
+            JSValueUnprotect(jsContext, removeNotificationHandler->value->notificationFunctionCallback());
+            notificationHandlers.remove(removeNotificationHandler);
+        }
+    }
+}
+
+void AccessibilityNotificationHandler::connectAccessibilityCallbacks()
+{
+    // Ensure no callbacks are connected before.
+    if (!disconnectAccessibilityCallbacks())
+        return;
+
+    // Add global listeners for AtkObject's signals.
+    stateChangeListenerId = atk_add_global_event_listener(axObjectEventListener, "ATK:AtkObject:state-change");
+    focusEventListenerId = atk_add_global_event_listener(axObjectEventListener, "ATK:AtkObject:focus-event");
+    activeDescendantChangedListenerId = atk_add_global_event_listener(axObjectEventListener, "ATK:AtkObject:active-descendant-changed");
+    childrenChangedListenerId = atk_add_global_event_listener(axObjectEventListener, "ATK:AtkObject:children-changed");
+    propertyChangedListenerId = atk_add_global_event_listener(axObjectEventListener, "ATK:AtkObject:property-change");
+    visibleDataChangedListenerId = atk_add_global_event_listener(axObjectEventListener, "ATK:AtkObject:visible-data-changed");
+}
+
+bool AccessibilityNotificationHandler::disconnectAccessibilityCallbacks()
+{
+    // Only disconnect if logging is off and there is no notification handler.
+    if (loggingAccessibilityEvents || !notificationHandlers.isEmpty() || globalNotificationHandler)
+        return false;
+
+    // AtkObject signals.
+    if (stateChangeListenerId) {
+        atk_remove_global_event_listener(stateChangeListenerId);
+        stateChangeListenerId = 0;
+    }
+    if (focusEventListenerId) {
+        atk_remove_global_event_listener(focusEventListenerId);
+        focusEventListenerId = 0;
+    }
+    if (activeDescendantChangedListenerId) {
+        atk_remove_global_event_listener(activeDescendantChangedListenerId);
+        activeDescendantChangedListenerId = 0;
+    }
+    if (childrenChangedListenerId) {
+        atk_remove_global_event_listener(childrenChangedListenerId);
+        childrenChangedListenerId = 0;
+    }
+    if (propertyChangedListenerId) {
+        atk_remove_global_event_listener(propertyChangedListenerId);
+        propertyChangedListenerId = 0;
+    }
+    if (visibleDataChangedListenerId) {
+        atk_remove_global_event_listener(visibleDataChangedListenerId);
+        visibleDataChangedListenerId = 0;
+    }
+
+    return true;
+}
+
+} // namespace WTR
diff --git a/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.h b/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.h
new file mode 100644 (file)
index 0000000..d79dec2
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2013 Samsung Electronics Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef AccessibilityNotificationHandlerAtk_h
+#define AccessibilityNotificationHandlerAtk_h
+
+#include <JavaScriptCore/JSObjectRef.h>
+#include <atk/atk.h>
+#include <atk/atkobject.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/gobject/GRefPtr.h>
+
+namespace WTR {
+
+class AccessibilityNotificationHandler : public RefCounted<AccessibilityNotificationHandler> {
+public:
+    static PassRefPtr<AccessibilityNotificationHandler> create()
+    {
+        return adoptRef(new AccessibilityNotificationHandler());
+    }
+    ~AccessibilityNotificationHandler();
+    void setPlatformElement(GRefPtr<AtkObject> platformElement) { m_platformElement = platformElement; }
+    GRefPtr<AtkObject> platformElement() const { return m_platformElement; }
+    void setNotificationFunctionCallback(JSValueRef);
+    JSValueRef notificationFunctionCallback() const { return m_notificationFunctionCallback; }
+    void logAccessibilityEvents();
+
+private:
+    AccessibilityNotificationHandler();
+    void connectAccessibilityCallbacks();
+    bool disconnectAccessibilityCallbacks();
+    void removeAccessibilityNotificationHandler();
+
+    GRefPtr<AtkObject> m_platformElement;
+    JSValueRef m_notificationFunctionCallback;
+};
+
+} // namespace WTR
+
+#endif // AccessibilityNotificationHandlerAtk_h
index 3e70fe46bb5444290e733f7a59dda6c24c6456c5..4bcfcebdfbd5d44e46297e493258d17a6c0e6410 100644 (file)
@@ -1119,13 +1119,26 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::url()
 
 bool AccessibilityUIElement::addNotificationListener(JSValueRef functionCallback)
 {
-    // FIXME: implement
+    if (!functionCallback)
+        return false;
+
+    // Only one notification listener per element.
+    if (m_notificationHandler)
+        return false;
+
+    m_notificationHandler = AccessibilityNotificationHandler::create();
+    m_notificationHandler->setPlatformElement(platformUIElement());
+    m_notificationHandler->setNotificationFunctionCallback(functionCallback);
+
     return true;
 }
 
 bool AccessibilityUIElement::removeNotificationListener()
 {
-    // FIXME: implement
+    // Programmers should not be trying to remove a listener that's already removed.
+    ASSERT(m_notificationHandler);
+    m_notificationHandler = 0;
+
     return true;
 }
 
index 50aefca07655b7c135f8a865790555bf072f78b0..9d26d89398fc2f6727c4b37cbda7c3a3b2fe5174 100644 (file)
@@ -70,6 +70,7 @@ add_definitions(-DFONTS_CONF_DIR="${TOOLS_DIR}/DumpRenderTree/gtk/fonts"
 if (ENABLE_ACCESSIBILITY)
     list(APPEND WebKitTestRunnerInjectedBundle_SOURCES
         ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/atk/AccessibilityControllerAtk.cpp
+        ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/atk/AccessibilityNotificationHandlerAtk.cpp
         ${WEBKIT_TESTRUNNER_INJECTEDBUNDLE_DIR}/atk/AccessibilityUIElementAtk.cpp
     )
     list(APPEND WebKitTestRunner_INCLUDE_DIRECTORIES