blob: aa23aa40f3d9f7d47586ac61e512f6e33a9371c8 [file] [log] [blame]
/*
* Copyright (C) 2018 Igalia S.L.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "ViewBackend.h"
#if defined(ENABLE_ACCESSIBILITY) && ENABLE_ACCESSIBILITY
#include "WebKitAccessibleApplication.h"
#include <atk-bridge.h>
#include <atk/atk.h>
#include <glib.h>
namespace WPEToolingBackends {
static GHashTable* keyEventListeners;
struct KeyEventListener {
AtkKeySnoopFunc function;
gpointer userData;
};
static unsigned addKeyEventListener(AtkKeySnoopFunc function, gpointer userData)
{
if (!keyEventListeners) {
keyEventListeners = g_hash_table_new_full(g_direct_hash, g_direct_equal, nullptr, [](gpointer data) {
delete static_cast<KeyEventListener*>(data);
});
}
static unsigned key = 0;
key++;
g_hash_table_insert(keyEventListeners, GUINT_TO_POINTER(key), new KeyEventListener { function, userData });
return key;
}
static void removeKeyEventListener(unsigned key)
{
if (!keyEventListeners)
return;
g_hash_table_remove(keyEventListeners, GUINT_TO_POINTER(key));
}
void ViewBackend::notifyAccessibilityKeyEventListeners(struct wpe_input_keyboard_event* event)
{
if (!keyEventListeners)
return;
AtkKeyEventStruct atkEvent;
atkEvent.type = event->pressed ? ATK_KEY_EVENT_PRESS : ATK_KEY_EVENT_RELEASE;
atkEvent.state = event->modifiers;
atkEvent.keyval = event->key_code;
atkEvent.keycode = event->hardware_key_code;
atkEvent.timestamp = event->time;
atkEvent.string = nullptr;
switch (atkEvent.keyval) {
case WPE_KEY_ISO_Enter:
case WPE_KEY_KP_Enter:
case WPE_KEY_Return:
atkEvent.string = g_strdup("\r");
atkEvent.length = 1;
break;
case WPE_KEY_BackSpace:
atkEvent.string = g_strdup("\x8");
atkEvent.length = 1;
break;
case WPE_KEY_Tab:
atkEvent.string = g_strdup("\t");
atkEvent.length = 1;
break;
default:
break;
}
if (!atkEvent.string) {
auto unicodeCharacter = wpe_key_code_to_unicode(atkEvent.keyval);
if (unicodeCharacter) {
char buffer[7];
int length = g_unichar_to_utf8(unicodeCharacter, buffer);
buffer[length] = '\0';
size_t bytesWritten;
atkEvent.string = g_locale_from_utf8(buffer, length, nullptr, &bytesWritten, nullptr);
atkEvent.length = bytesWritten;
}
}
if (!atkEvent.string) {
atkEvent.length = 0;
atkEvent.string = g_strdup("");
}
GHashTableIter iter;
gpointer value;
g_hash_table_iter_init(&iter, keyEventListeners);
while (g_hash_table_iter_next(&iter, nullptr, &value)) {
auto* listener = static_cast<KeyEventListener*>(value);
listener->function(&atkEvent, listener->userData);
}
g_free(atkEvent.string);
}
void ViewBackend::initializeAccessibility()
{
auto* atkUtilClass = ATK_UTIL_CLASS(g_type_class_ref(ATK_TYPE_UTIL));
atkUtilClass->add_key_event_listener = [](AtkKeySnoopFunc listener, gpointer userData) -> guint {
return addKeyEventListener(listener, userData);
};
atkUtilClass->remove_key_event_listener = [](guint key) {
removeKeyEventListener(key);
};
atkUtilClass->get_root = []() -> AtkObject* {
static AtkObject* accessible = nullptr;
if (!accessible)
accessible = ATK_OBJECT(webkitAccessibleApplicationNew());
return accessible;
};
atkUtilClass->get_toolkit_name = []() -> const gchar* {
return "WPEWebKit";
};
atkUtilClass->get_toolkit_version = []() -> const gchar* {
return "";
};
atk_bridge_adaptor_init(nullptr, nullptr);
}
void ViewBackend::updateAccessibilityState(uint32_t previousFlags)
{
auto* accessible = atk_get_root();
if (!WEBKIT_IS_ACCESSIBLE_APPLICATION(accessible))
return;
uint32_t flags = wpe_view_backend_get_activity_state(backend());
uint32_t changedFlags = previousFlags ^ flags;
if (changedFlags & wpe_view_activity_state_in_window)
atk_object_notify_state_change(accessible, ATK_STATE_ACTIVE, flags & wpe_view_activity_state_in_window);
if (changedFlags & wpe_view_activity_state_visible)
atk_object_notify_state_change(accessible, ATK_STATE_VISIBLE, flags & wpe_view_activity_state_visible);
if (changedFlags & wpe_view_activity_state_focused) {
atk_object_notify_state_change(accessible, ATK_STATE_FOCUSED, flags & wpe_view_activity_state_focused);
if ((flags & wpe_view_activity_state_in_window) && (flags & wpe_view_activity_state_focused))
g_signal_emit_by_name(accessible, "activate");
else
g_signal_emit_by_name(accessible, "deactivate");
}
}
void ViewBackend::setAccessibleChild(AtkObject* child)
{
auto* accessible = atk_get_root();
if (!WEBKIT_IS_ACCESSIBLE_APPLICATION(accessible))
return;
webkitAccessibleApplicationSetChild(WEBKIT_ACCESSIBLE_APPLICATION(accessible), child);
}
} // namespace WPEToolingBackends
#endif // ENABLE(ACCESSIBILITY)