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
+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
${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
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 \
-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 \
}
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
{
}
#if PLATFORM(WIN)
#include <windows.h>
#endif
+#if PLATFORM(GTK) || (PLATFORM(EFL) && HAVE(ACCESSIBILITY))
+#include "AccessibilityNotificationHandlerAtk.h"
+#endif
namespace WTR {
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
};
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;
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
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(¶mValues[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(¶mValues[1])));
- signalValue.set(g_strdup_printf("%d", g_value_get_boolean(¶mValues[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(¶mValues[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(¶mValues[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.
// 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.
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)
--- /dev/null
+/*
+ * 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(¶mValues[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(¶mValues[1])));
+ signalValue.set(g_strdup_printf("%d", g_value_get_boolean(¶mValues[2])));
+ if (!g_strcmp0(g_value_get_string(¶mValues[1]), "checked"))
+ notificationName = "CheckedStateChanged";
+ else if (!g_strcmp0(g_value_get_string(¶mValues[1]), "invalid-entry"))
+ notificationName = "AXInvalidStatusChanged";
+ else if (!g_strcmp0(g_value_get_string(¶mValues[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(¶mValues[1])));
+ if (g_value_get_boolean(¶mValues[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(¶mValues[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, ¬ificationNameArgument, 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
--- /dev/null
+/*
+ * 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
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;
}
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