Title: [287070] trunk
Revision
287070
Author
carlo...@webkit.org
Date
2021-12-15 05:48:20 -0800 (Wed, 15 Dec 2021)

Log Message

[GTK][a11y] Register the wrappers tree when there's an event listener registered
https://bugs.webkit.org/show_bug.cgi?id=234338

Reviewed by Joanmarie Diggs.

Source/WebCore:

Connect to the atspi registry to get the list of registered event listeners. If there's any listener, we register
the tree to ensure we can emit signals, but we only emit the signals for which there's a listener registered,
except for the ones required by the atspi cache.

* accessibility/atspi/AccessibilityAtspi.cpp:
(WebCore::AccessibilityAtspi::AccessibilityAtspi): Connect to the registry.
(WebCore::AccessibilityAtspi::~AccessibilityAtspi): Disconnect registry signals.
(WebCore::AccessibilityAtspi::registerTrees const): Register the trees of all root objects.
(WebCore::AccessibilityAtspi::initializeRegistry): Get the list of listeners registered and connect to
EventListenerRegistered signal.
(WebCore::AccessibilityAtspi::addEventListener): Add an event listener to the list converting the detail to non
camel case to be abel to compare it later on signal emission.
(WebCore::eventIsSubtype): Helper to check if event type is subtype of another.
(WebCore::AccessibilityAtspi::removeEventListener): Remove and events listener from the list.
(WebCore::AccessibilityAtspi::shouldEmitSignal): Return whether the given signal should be emitted, checking the
registered event listeners.
(WebCore::AccessibilityAtspi::parentChanged): Do not emit the signal if the object hasn't been registered yet to
avoid flooding during tree creation. We just register the object the object in that case which will update the
cache because addAccessible is always called.
(WebCore::AccessibilityAtspi::stateChanged): Return early if signal should not be emitted.
(WebCore::AccessibilityAtspi::textChanged): Ditto.
(WebCore::AccessibilityAtspi::textAttributesChanged): Ditto.
(WebCore::AccessibilityAtspi::textCaretMoved): Ditto.
(WebCore::AccessibilityAtspi::textSelectionChanged): Ditto.
(WebCore::AccessibilityAtspi::valueChanged): Ditto.
(WebCore::AccessibilityAtspi::selectionChanged): Ditto.
* accessibility/atspi/AccessibilityAtspi.h:
(WebCore::AccessibilityAtspi::hasEventListeners const):
* accessibility/atspi/AccessibilityObjectAtspi.cpp:
(WebCore::AccessibilityObjectAtspi::childAdded): Return early if object is already detached or ignored.
(WebCore::AccessibilityObjectAtspi::childRemoved): Ditto.
(WebCore::AccessibilityObjectAtspi::stateChanged): Do not return early if the object is not registered because
we might end up emitting the signal.
* accessibility/atspi/AccessibilityObjectSelectionAtspi.cpp:
(WebCore::AccessibilityObjectAtspi::selectionChanged): Ditto.
* accessibility/atspi/AccessibilityObjectTextAtspi.cpp:
(WebCore::AccessibilityObjectAtspi::textInserted): Ditto.
(WebCore::AccessibilityObjectAtspi::textDeleted): Ditto.
(WebCore::AccessibilityObjectAtspi::selectionChanged): Ditto.
(WebCore::AccessibilityObjectAtspi::textAttributesChanged): Ditto.
* accessibility/atspi/AccessibilityObjectValueAtspi.cpp:
(WebCore::AccessibilityObjectAtspi::valueChanged): Ditto.
* accessibility/atspi/AccessibilityRootAtspi.cpp:
(WebCore::AccessibilityRootAtspi::embedded): Register the tree if there are event listeners registered.

Tools:

Add unit test to check the tree is created when there's a listener connected.

* TestWebKitAPI/Tests/WebKitGtk/TestWebKitAccessibility.cpp:
(AccessibilityTest::findTestApplication): Iterate the applications list backwards since the test program is
usually the last one in the list.
(AccessibilityTest::accessibleApplicationIsTestProgram): Helper to check if a given accessible belongs to the
test program.
(AccessibilityTest::startEventMonitor): Support for listening to events associated to the test program, not to a
particular accessible.
(testAccessibleEventListener):
(beforeAll):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (287069 => 287070)


--- trunk/Source/WebCore/ChangeLog	2021-12-15 10:16:10 UTC (rev 287069)
+++ trunk/Source/WebCore/ChangeLog	2021-12-15 13:48:20 UTC (rev 287070)
@@ -1,3 +1,55 @@
+2021-12-15  Carlos Garcia Campos  <cgar...@igalia.com>
+
+        [GTK][a11y] Register the wrappers tree when there's an event listener registered
+        https://bugs.webkit.org/show_bug.cgi?id=234338
+
+        Reviewed by Joanmarie Diggs.
+
+        Connect to the atspi registry to get the list of registered event listeners. If there's any listener, we register
+        the tree to ensure we can emit signals, but we only emit the signals for which there's a listener registered,
+        except for the ones required by the atspi cache.
+
+        * accessibility/atspi/AccessibilityAtspi.cpp:
+        (WebCore::AccessibilityAtspi::AccessibilityAtspi): Connect to the registry.
+        (WebCore::AccessibilityAtspi::~AccessibilityAtspi): Disconnect registry signals.
+        (WebCore::AccessibilityAtspi::registerTrees const): Register the trees of all root objects.
+        (WebCore::AccessibilityAtspi::initializeRegistry): Get the list of listeners registered and connect to
+        EventListenerRegistered signal.
+        (WebCore::AccessibilityAtspi::addEventListener): Add an event listener to the list converting the detail to non
+        camel case to be abel to compare it later on signal emission.
+        (WebCore::eventIsSubtype): Helper to check if event type is subtype of another.
+        (WebCore::AccessibilityAtspi::removeEventListener): Remove and events listener from the list.
+        (WebCore::AccessibilityAtspi::shouldEmitSignal): Return whether the given signal should be emitted, checking the
+        registered event listeners.
+        (WebCore::AccessibilityAtspi::parentChanged): Do not emit the signal if the object hasn't been registered yet to
+        avoid flooding during tree creation. We just register the object the object in that case which will update the
+        cache because addAccessible is always called.
+        (WebCore::AccessibilityAtspi::stateChanged): Return early if signal should not be emitted.
+        (WebCore::AccessibilityAtspi::textChanged): Ditto.
+        (WebCore::AccessibilityAtspi::textAttributesChanged): Ditto.
+        (WebCore::AccessibilityAtspi::textCaretMoved): Ditto.
+        (WebCore::AccessibilityAtspi::textSelectionChanged): Ditto.
+        (WebCore::AccessibilityAtspi::valueChanged): Ditto.
+        (WebCore::AccessibilityAtspi::selectionChanged): Ditto.
+        * accessibility/atspi/AccessibilityAtspi.h:
+        (WebCore::AccessibilityAtspi::hasEventListeners const):
+        * accessibility/atspi/AccessibilityObjectAtspi.cpp:
+        (WebCore::AccessibilityObjectAtspi::childAdded): Return early if object is already detached or ignored.
+        (WebCore::AccessibilityObjectAtspi::childRemoved): Ditto.
+        (WebCore::AccessibilityObjectAtspi::stateChanged): Do not return early if the object is not registered because
+        we might end up emitting the signal.
+        * accessibility/atspi/AccessibilityObjectSelectionAtspi.cpp:
+        (WebCore::AccessibilityObjectAtspi::selectionChanged): Ditto.
+        * accessibility/atspi/AccessibilityObjectTextAtspi.cpp:
+        (WebCore::AccessibilityObjectAtspi::textInserted): Ditto.
+        (WebCore::AccessibilityObjectAtspi::textDeleted): Ditto.
+        (WebCore::AccessibilityObjectAtspi::selectionChanged): Ditto.
+        (WebCore::AccessibilityObjectAtspi::textAttributesChanged): Ditto.
+        * accessibility/atspi/AccessibilityObjectValueAtspi.cpp:
+        (WebCore::AccessibilityObjectAtspi::valueChanged): Ditto.
+        * accessibility/atspi/AccessibilityRootAtspi.cpp:
+        (WebCore::AccessibilityRootAtspi::embedded): Register the tree if there are event listeners registered.
+
 2021-12-15  Youenn Fablet  <you...@apple.com>
 
         Make sure to start a realtime outgoing source in case it is taken to another sender

Modified: trunk/Source/WebCore/accessibility/atspi/AccessibilityAtspi.cpp (287069 => 287070)


--- trunk/Source/WebCore/accessibility/atspi/AccessibilityAtspi.cpp	2021-12-15 10:16:10 UTC (rev 287069)
+++ trunk/Source/WebCore/accessibility/atspi/AccessibilityAtspi.cpp	2021-12-15 13:48:20 UTC (rev 287070)
@@ -29,7 +29,6 @@
 #include <wtf/SetForScope.h>
 #include <wtf/SortedArrayMap.h>
 #include <wtf/UUID.h>
-#include <wtf/glib/GUniquePtr.h>
 
 namespace WebCore {
 
@@ -44,11 +43,164 @@
         m_connection = adoptGRef(g_dbus_connection_new_for_address_sync(busAddress.utf8().data(),
             static_cast<GDBusConnectionFlags>(G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION),
             nullptr, nullptr, &error.outPtr()));
-        if (!m_connection)
+
+        if (m_connection)
+            initializeRegistry();
+        else
             g_warning("Can't connect to a11y bus: %s", error->message);
     });
 }
 
+AccessibilityAtspi::~AccessibilityAtspi()
+{
+    if (m_registry)
+        g_signal_handlers_disconnect_by_data(m_registry.get(), this);
+}
+
+void AccessibilityAtspi::registerTrees() const
+{
+    RELEASE_ASSERT(!isMainThread());
+    for (auto* rootObject : m_rootObjects.keys()) {
+        if (!rootObject->isTreeRegistered())
+            rootObject->registerTree();
+    }
+}
+
+void AccessibilityAtspi::initializeRegistry()
+{
+    RELEASE_ASSERT(!isMainThread());
+    RELEASE_ASSERT(m_connection);
+    GUniqueOutPtr<GError> error;
+    m_registry = adoptGRef(g_dbus_proxy_new_sync(m_connection.get(), G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, nullptr,
+        "org.a11y.atspi.Registry", "/org/a11y/atspi/registry", "org.a11y.atspi.Registry", nullptr, &error.outPtr()));
+    if (!m_registry) {
+        g_warning("Failed to connect to atspi registry: %s\n", error->message);
+        return;
+    }
+
+    g_signal_connect(m_registry.get(), "g-signal", G_CALLBACK(+[](GDBusProxy*, char*, char* signal, GVariant* parameters, AccessibilityAtspi* atspi) {
+        const char* dbusName;
+        const char* eventName;
+        if (!g_strcmp0(signal, "EventListenerRegistered")) {
+            g_variant_get(parameters, "(&s&s@as)", &dbusName, &eventName, nullptr);
+            atspi->addEventListener(dbusName, eventName);
+            atspi->registerTrees();
+        } else if (!g_strcmp0(signal, "EventListenerDeregistered")) {
+            g_variant_get(parameters, "(&s&s)", &dbusName, &eventName);
+            atspi->removeEventListener(dbusName, eventName);
+        }
+    }), this);
+
+    GRefPtr<GVariant> result = adoptGRef(g_dbus_proxy_call_sync(m_registry.get(), "GetRegisteredEvents", nullptr, G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &error.outPtr()));
+    if (!result) {
+        g_warning("Failed to get atspi registered event listeners: %s\n", error->message);
+        return;
+    }
+
+    GRefPtr<GVariant> events;
+    g_variant_get(result.get(), "(@a(ss))", &events.outPtr());
+    GVariantIter iter;
+    auto eventCount = g_variant_iter_init(&iter, events.get());
+    const char* dbusName;
+    const char* eventName;
+    while (g_variant_iter_loop(&iter, "(&s&s)", &dbusName, &eventName))
+        addEventListener(dbusName, eventName);
+
+    if (eventCount)
+        registerTrees();
+}
+
+static GUniquePtr<char*> eventConvertingDetailToNonCamelCase(const char* eventName)
+{
+    GUniquePtr<char*> event(g_strsplit(eventName, ":", 3));
+    if (!event.get()[0] || !event.get()[1] || !event.get()[2] || !*event.get()[2])
+        return event;
+
+    char* converted = static_cast<char*>(g_malloc(strlen(event.get()[2]) * 2 + 1));
+    char* convertedPtr = converted;
+    char* detailPtr = event.get()[2];
+
+    while (*detailPtr) {
+        if (isASCIIUpper(*detailPtr)) {
+            if (convertedPtr > converted)
+                *convertedPtr++ = '-';
+            *convertedPtr++ = toASCIILower(*detailPtr++);
+        } else
+            *convertedPtr++ = *detailPtr++;
+    }
+    *convertedPtr = '\0';
+
+    g_free(event.get()[2]);
+    event.get()[2] = converted;
+
+    return event;
+}
+
+void AccessibilityAtspi::addEventListener(const char* dbusName, const char* eventName)
+{
+    RELEASE_ASSERT(!isMainThread());
+    auto& listeners = m_eventListeners.ensure(dbusName, [] {
+        return Vector<GUniquePtr<char*>> { };
+    }).iterator->value;
+    listeners.append(eventConvertingDetailToNonCamelCase(eventName));
+}
+
+static bool eventIsSubtype(char** needle, char** haystack)
+{
+    while (*haystack && **haystack) {
+        if (g_strcmp0(*needle, *haystack))
+            return false;
+        needle++;
+        haystack++;
+    }
+
+    return true;
+}
+
+void AccessibilityAtspi::removeEventListener(const char* dbusName, const char* eventName)
+{
+    RELEASE_ASSERT(!isMainThread());
+    if (!eventName || !*eventName) {
+        m_eventListeners.remove(dbusName);
+        return;
+    }
+
+    auto it = m_eventListeners.find(dbusName);
+    if (it == m_eventListeners.end())
+        return;
+
+    auto needle = eventConvertingDetailToNonCamelCase(eventName);
+    it->value.removeAllMatching([&](const GUniquePtr<char*>& event) {
+        return eventIsSubtype(needle.get(), event.get());
+    });
+
+    if (it->value.isEmpty())
+        m_eventListeners.remove(it);
+}
+
+bool AccessibilityAtspi::shouldEmitSignal(const char* interface, const char* name, const char* detail)
+{
+    RELEASE_ASSERT(!isMainThread());
+
+    // Always emit signals if we couldn't connect to the registry.
+    if (!m_registry)
+        return true;
+
+    if (m_eventListeners.isEmpty())
+        return false;
+
+    const char* needle[4] = { interface, name, detail, nullptr };
+    for (const auto& listeners : m_eventListeners.values()) {
+        auto result = listeners.findMatching([&](const GUniquePtr<char*>& event) {
+            return eventIsSubtype(const_cast<char**>(needle), event.get());
+        });
+        if (result != notFound)
+            return true;
+    }
+
+    return false;
+}
+
 RunLoop& AccessibilityAtspi::runLoop() const
 {
     return m_queue->runLoop();
@@ -180,9 +332,12 @@
         if (!atspiObject->root().isTreeRegistered())
             return;
 
-        // We call path here to ensure it happens before parentReference() in case objects are not registered yet.
-        auto path = atspiObject->path();
-        g_dbus_connection_emit_signal(m_connection.get(), nullptr, path.utf8().data(), "org.a11y.atspi.Event.Object", "PropertyChange",
+        // Emit parentChanged only if the object is already registered, otherwise register the object,
+        // without emitting the signal, because org.a11y.atspi.Cache.AddAccessible() will update the cache.
+        if (atspiObject->registerObject())
+            return;
+
+        g_dbus_connection_emit_signal(m_connection.get(), nullptr, atspiObject->path().utf8().data(), "org.a11y.atspi.Event.Object", "PropertyChange",
             g_variant_new("(siiva{sv})", "accessible-parent", 0, 0, atspiObject->parentReference(), nullptr), nullptr);
     });
 }
@@ -211,6 +366,9 @@
         if (!m_connection)
             return;
 
+        if (!shouldEmitSignal("Object", "StateChanged", name.data()))
+            return;
+
         g_dbus_connection_emit_signal(m_connection.get(), nullptr, atspiObject->path().utf8().data(), "org.a11y.atspi.Event.Object", "StateChanged",
             g_variant_new("(siiva{sv})", name.data(), value, 0, g_variant_new_string("0"), nullptr), nullptr);
     });
@@ -223,6 +381,9 @@
         if (!m_connection)
             return;
 
+        if (!shouldEmitSignal("Object", "TextChanged", changeType.data()))
+            return;
+
         g_dbus_connection_emit_signal(m_connection.get(), nullptr, atspiObject->path().utf8().data(), "org.a11y.atspi.Event.Object", "TextChanged",
             g_variant_new("(siiva{sv})", changeType.data(), offset, length, g_variant_new_string(text.data()), nullptr), nullptr);
     });
@@ -235,6 +396,9 @@
         if (!m_connection)
             return;
 
+        if (!shouldEmitSignal("Object", "TextAttributesChanged"))
+            return;
+
         g_dbus_connection_emit_signal(m_connection.get(), nullptr, atspiObject->path().utf8().data(), "org.a11y.atspi.Event.Object", "TextAttributesChanged",
             g_variant_new("(siiva{sv})", "", 0, 0, g_variant_new_string(""), nullptr), nullptr);
     });
@@ -247,6 +411,9 @@
         if (!m_connection)
             return;
 
+        if (!shouldEmitSignal("Object", "TextCaretMoved"))
+            return;
+
         g_dbus_connection_emit_signal(m_connection.get(), nullptr, atspiObject->path().utf8().data(), "org.a11y.atspi.Event.Object", "TextCaretMoved",
             g_variant_new("(siiva{sv})", "", caretOffset, 0, g_variant_new_string(""), nullptr), nullptr);
     });
@@ -259,6 +426,9 @@
         if (!m_connection)
             return;
 
+        if (!shouldEmitSignal("Object", "TextSelectionChanged"))
+            return;
+
         g_dbus_connection_emit_signal(m_connection.get(), nullptr, atspiObject->path().utf8().data(), "org.a11y.atspi.Event.Object", "TextSelectionChanged",
             g_variant_new("(siiva{sv})", "", 0, 0, g_variant_new_string(""), nullptr), nullptr);
     });
@@ -271,6 +441,9 @@
         if (!m_connection)
             return;
 
+        if (!shouldEmitSignal("Object", "PropertyChange", "accessible-value"))
+            return;
+
         g_dbus_connection_emit_signal(m_connection.get(), nullptr, atspiObject->path().utf8().data(), "org.a11y.atspi.Event.Object", "PropertyChange",
             g_variant_new("(siiva{sv})", "accessible-value", 0, 0, g_variant_new_double(value), nullptr), nullptr);
     });
@@ -283,6 +456,9 @@
         if (!m_connection)
             return;
 
+        if (!shouldEmitSignal("Object", "SelectionChanged"))
+            return;
+
         g_dbus_connection_emit_signal(m_connection.get(), nullptr, atspiObject->path().utf8().data(), "org.a11y.atspi.Event.Object", "SelectionChanged",
             g_variant_new("(siiva{sv})", "", 0, 0, g_variant_new_string(""), nullptr), nullptr);
     });

Modified: trunk/Source/WebCore/accessibility/atspi/AccessibilityAtspi.h (287069 => 287070)


--- trunk/Source/WebCore/accessibility/atspi/AccessibilityAtspi.h	2021-12-15 10:16:10 UTC (rev 287069)
+++ trunk/Source/WebCore/accessibility/atspi/AccessibilityAtspi.h	2021-12-15 13:48:20 UTC (rev 287070)
@@ -26,10 +26,12 @@
 #include <wtf/Vector.h>
 #include <wtf/WorkQueue.h>
 #include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
 
 typedef struct _GDBusConnection GDBusConnection;
 typedef struct _GDBusInterfaceInfo GDBusInterfaceInfo;
 typedef struct _GDBusInterfaceVTable GDBusInterfaceVTable;
+typedef struct _GDBusProxy GDBusProxy;
 typedef struct _GVariant GVariant;
 
 namespace WebCore {
@@ -41,12 +43,13 @@
     WTF_MAKE_FAST_ALLOCATED;
 public:
     AccessibilityAtspi(const String&);
-    ~AccessibilityAtspi() = default;
+    ~AccessibilityAtspi();
 
     WEBCORE_EXPORT RunLoop& runLoop() const;
 
     const char* uniqueName() const;
     GVariant* nullReference() const;
+    bool hasEventListeners() const { return !m_eventListeners.isEmpty(); }
 
     void registerRoot(AccessibilityRootAtspi&, Vector<std::pair<GDBusInterfaceInfo*, GDBusInterfaceVTable*>>&&, CompletionHandler<void(const String&)>&&);
     void unregisterRoot(AccessibilityRootAtspi&);
@@ -74,13 +77,22 @@
     void addAccessible(AccessibilityObjectAtspi&);
 
 private:
+    void registerTrees() const;
+    void initializeRegistry();
+    void addEventListener(const char* dbusName, const char* eventName);
+    void removeEventListener(const char* dbusName, const char* eventName);
+
     void ensureCache();
     void removeAccessible(AccessibilityObjectAtspi&);
 
+    bool shouldEmitSignal(const char* interface, const char* name, const char* detail = "");
+
     static GDBusInterfaceVTable s_cacheFunctions;
 
     Ref<WorkQueue> m_queue;
     GRefPtr<GDBusConnection> m_connection;
+    GRefPtr<GDBusProxy> m_registry;
+    HashMap<CString, Vector<GUniquePtr<char*>>> m_eventListeners;
     HashMap<AccessibilityRootAtspi*, Vector<unsigned, 2>> m_rootObjects;
     HashMap<AccessibilityObjectAtspi*, Vector<unsigned, 20>> m_atspiObjects;
     HashMap<AccessibilityObjectAtspi*, Vector<unsigned, 20>> m_atspiHyperlinks;

Modified: trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.cpp (287069 => 287070)


--- trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.cpp	2021-12-15 10:16:10 UTC (rev 287069)
+++ trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.cpp	2021-12-15 13:48:20 UTC (rev 287070)
@@ -1213,6 +1213,9 @@
     if (!m_isRegistered.load())
         return;
 
+    if (!m_coreObject || m_coreObject->accessibilityIsIgnored())
+        return;
+
     m_root.atspi().childrenChanged(*this, child, AccessibilityAtspi::ChildrenChanged::Added);
 }
 
@@ -1222,6 +1225,9 @@
     if (!m_isRegistered.load())
         return;
 
+    if (!m_coreObject || m_coreObject->accessibilityIsIgnored())
+        return;
+
     m_root.atspi().childrenChanged(*this, child, AccessibilityAtspi::ChildrenChanged::Removed);
 }
 
@@ -1228,9 +1234,6 @@
 void AccessibilityObjectAtspi::stateChanged(const char* name, bool value)
 {
     RELEASE_ASSERT(isMainThread());
-    if (!m_isRegistered.load())
-        return;
-
     m_root.atspi().stateChanged(*this, name, value);
 }
 

Modified: trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectSelectionAtspi.cpp (287069 => 287070)


--- trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectSelectionAtspi.cpp	2021-12-15 10:16:10 UTC (rev 287069)
+++ trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectSelectionAtspi.cpp	2021-12-15 13:48:20 UTC (rev 287070)
@@ -224,9 +224,6 @@
 void AccessibilityObjectAtspi::selectionChanged()
 {
     RELEASE_ASSERT(isMainThread());
-    if (!m_isRegistered.load())
-        return;
-
     m_root.atspi().selectionChanged(*this);
 }
 

Modified: trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectTextAtspi.cpp (287069 => 287070)


--- trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectTextAtspi.cpp	2021-12-15 10:16:10 UTC (rev 287069)
+++ trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectTextAtspi.cpp	2021-12-15 13:48:20 UTC (rev 287070)
@@ -348,9 +348,6 @@
 void AccessibilityObjectAtspi::textInserted(const String& insertedText, const VisiblePosition& position)
 {
     RELEASE_ASSERT(isMainThread());
-    if (!m_isRegistered.load())
-        return;
-
     if (!m_interfaces.contains(Interface::Text))
         return;
 
@@ -367,9 +364,6 @@
 void AccessibilityObjectAtspi::textDeleted(const String& deletedText, const VisiblePosition& position)
 {
     RELEASE_ASSERT(isMainThread());
-    if (!m_isRegistered.load())
-        return;
-
     if (!m_interfaces.contains(Interface::Text))
         return;
 
@@ -751,9 +745,6 @@
 void AccessibilityObjectAtspi::selectionChanged(const VisibleSelection& selection)
 {
     RELEASE_ASSERT(isMainThread());
-    if (!m_isRegistered.load())
-        return;
-
     if (!m_interfaces.contains(Interface::Text))
         return;
 
@@ -943,9 +934,6 @@
 void AccessibilityObjectAtspi::textAttributesChanged()
 {
     RELEASE_ASSERT(isMainThread());
-    if (!m_isRegistered.load())
-        return;
-
     if (!m_interfaces.contains(Interface::Text))
         return;
 

Modified: trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectValueAtspi.cpp (287069 => 287070)


--- trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectValueAtspi.cpp	2021-12-15 10:16:10 UTC (rev 287069)
+++ trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectValueAtspi.cpp	2021-12-15 13:48:20 UTC (rev 287070)
@@ -134,9 +134,6 @@
 void AccessibilityObjectAtspi::valueChanged(double value)
 {
     RELEASE_ASSERT(isMainThread());
-    if (!m_isRegistered.load())
-        return;
-
     m_root.atspi().valueChanged(*this, value);
 }
 

Modified: trunk/Source/WebCore/accessibility/atspi/AccessibilityRootAtspi.cpp (287069 => 287070)


--- trunk/Source/WebCore/accessibility/atspi/AccessibilityRootAtspi.cpp	2021-12-15 10:16:10 UTC (rev 287069)
+++ trunk/Source/WebCore/accessibility/atspi/AccessibilityRootAtspi.cpp	2021-12-15 13:48:20 UTC (rev 287070)
@@ -223,6 +223,8 @@
     RELEASE_ASSERT(!isMainThread());
     m_parentUniqueName = parentUniqueName;
     m_parentPath = parentPath;
+    if (!m_isTreeRegistered.load() && m_atspi.hasEventListeners())
+        registerTree();
 }
 
 GVariant* AccessibilityRootAtspi::applicationReference() const

Modified: trunk/Tools/ChangeLog (287069 => 287070)


--- trunk/Tools/ChangeLog	2021-12-15 10:16:10 UTC (rev 287069)
+++ trunk/Tools/ChangeLog	2021-12-15 13:48:20 UTC (rev 287070)
@@ -1,3 +1,22 @@
+2021-12-15  Carlos Garcia Campos  <cgar...@igalia.com>
+
+        [GTK][a11y] Register the wrappers tree when there's an event listener registered
+        https://bugs.webkit.org/show_bug.cgi?id=234338
+
+        Reviewed by Joanmarie Diggs.
+
+        Add unit test to check the tree is created when there's a listener connected.
+
+        * TestWebKitAPI/Tests/WebKitGtk/TestWebKitAccessibility.cpp:
+        (AccessibilityTest::findTestApplication): Iterate the applications list backwards since the test program is
+        usually the last one in the list.
+        (AccessibilityTest::accessibleApplicationIsTestProgram): Helper to check if a given accessible belongs to the
+        test program.
+        (AccessibilityTest::startEventMonitor): Support for listening to events associated to the test program, not to a
+        particular accessible.
+        (testAccessibleEventListener):
+        (beforeAll):
+
 2021-12-14  Don Olmstead  <don.olmst...@sony.com>
 
         [CMake] Modernize WPEToolingBackend

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitGtk/TestWebKitAccessibility.cpp (287069 => 287070)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitGtk/TestWebKitAccessibility.cpp	2021-12-15 10:16:10 UTC (rev 287069)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitGtk/TestWebKitAccessibility.cpp	2021-12-15 13:48:20 UTC (rev 287070)
@@ -57,9 +57,10 @@
         Test::removeLogFatalFlag(G_LOG_LEVEL_WARNING);
         int childCount = atspi_accessible_get_child_count(desktop.get(), nullptr);
         Test::addLogFatalFlag(G_LOG_LEVEL_WARNING);
-        for (int i = 0; i < childCount; ++i) {
+        for (int i = childCount - 1; i >= 0; --i)  {
             GRefPtr<AtspiAccessible> current = adoptGRef(atspi_accessible_get_child_at_index(desktop.get(), i, nullptr));
-            if (!g_strcmp0(atspi_accessible_get_name(current.get(), nullptr), "TestWebKitAccessibility"))
+            GUniquePtr<char> name(atspi_accessible_get_name(current.get(), nullptr));
+            if (!g_strcmp0(name.get(), "TestWebKitAccessibility"))
                 return current;
         }
 
@@ -106,6 +107,13 @@
         m_eventSource = nullptr;
     }
 
+    static bool accessibleApplicationIsTestProgram(AtspiAccessible* accessible)
+    {
+        GRefPtr<AtspiAccessible> application = adoptGRef(atspi_accessible_get_application(accessible, nullptr));
+        GUniquePtr<char> applicationName(atspi_accessible_get_name(application.get(), nullptr));
+        return !g_strcmp0(applicationName.get(), "TestWebKitAccessibility");
+    }
+
     void startEventMonitor(AtspiAccessible* source, Vector<CString>&& events)
     {
         m_eventMonitor.source = source;
@@ -112,8 +120,10 @@
         m_eventMonitor.eventTypes = WTFMove(events);
         m_eventMonitor.listener = adoptGRef(atspi_event_listener_new([](AtspiEvent* event, gpointer userData) {
             auto* test = static_cast<AccessibilityTest*>(userData);
-            if (event->source == test->m_eventMonitor.source)
+            if ((test->m_eventMonitor.source && event->source == test->m_eventMonitor.source)
+                || (!test->m_eventMonitor.source && accessibleApplicationIsTestProgram(event->source))) {
                 test->m_eventMonitor.events.append(static_cast<AtspiEvent*>(g_boxed_copy(ATSPI_TYPE_EVENT, event)));
+            }
         }, this, nullptr));
 
         for (const auto& event : m_eventMonitor.eventTypes)
@@ -811,6 +821,45 @@
 #endif
 }
 
+static void testAccessibleEventListener(AccessibilityTest* test, gconstpointer)
+{
+    test->startEventMonitor(nullptr, { "object:state-changed:focused" });
+    test->showInWindow();
+    test->loadHtml(
+        "<html>"
+        "  <body>"
+        "    <input id='entry' type='text'/>"
+        "  </body>"
+        "</html>",
+        nullptr);
+    test->waitUntilLoadFinished();
+
+    test->runJavaScriptAndWaitUntilFinished("document.getElementById('entry').focus();", nullptr);
+
+    auto events = test->stopEventMonitor(1);
+    g_assert_cmpuint(events.size(), ==, 1);
+    g_assert_cmpstr(events[0]->type, ==, "object:state-changed:focused");
+    auto* entry = events[0]->source;
+    g_assert_true(ATSPI_IS_ACCESSIBLE(entry));
+    g_assert_cmpint(atspi_accessible_get_role(entry, nullptr), ==, ATSPI_ROLE_ENTRY);
+
+    auto panel = adoptGRef(atspi_accessible_get_parent(entry, nullptr));
+    g_assert_true(ATSPI_IS_ACCESSIBLE(panel.get()));
+    g_assert_cmpint(atspi_accessible_get_role(panel.get(), nullptr), ==, ATSPI_ROLE_PANEL);
+
+    auto documentWeb = adoptGRef(atspi_accessible_get_parent(panel.get(), nullptr));
+    g_assert_true(ATSPI_IS_ACCESSIBLE(documentWeb.get()));
+    g_assert_cmpint(atspi_accessible_get_role(documentWeb.get(), nullptr), ==, ATSPI_ROLE_DOCUMENT_WEB);
+
+    auto scrollView = adoptGRef(atspi_accessible_get_parent(documentWeb.get(), nullptr));
+    g_assert_true(ATSPI_IS_ACCESSIBLE(scrollView.get()));
+    g_assert_cmpint(atspi_accessible_get_role(scrollView.get(), nullptr), ==, ATSPI_ROLE_SCROLL_PANE);
+
+    auto rootObject = adoptGRef(atspi_accessible_get_parent(scrollView.get(), nullptr));
+    g_assert_true(ATSPI_IS_ACCESSIBLE(rootObject.get()));
+    g_assert_cmpint(atspi_accessible_get_role(rootObject.get(), nullptr), ==, ATSPI_ROLE_FILLER);
+}
+
 static void testComponentHitTest(AccessibilityTest* test, gconstpointer)
 {
     test->showInWindow();
@@ -2752,6 +2801,7 @@
     AccessibilityTest::add("WebKitAccessibility", "accessible/attributes", testAccessibleAttributes);
     AccessibilityTest::add("WebKitAccessibility", "accessible/state", testAccessibleState);
     AccessibilityTest::add("WebKitAccessibility", "accessible/state-changed", testAccessibleStateChanged);
+    AccessibilityTest::add("WebKitAccessibility", "accessible/event-listener", testAccessibleEventListener);
     AccessibilityTest::add("WebKitAccessibility", "component/hit-test", testComponentHitTest);
 #ifdef ATSPI_SCROLLTYPE_COUNT
     AccessibilityTest::add("WebKitAccessibility", "component/scroll-to", testComponentScrollTo);
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to