⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 287070 in webkit


Ignore:
Timestamp:
Dec 15, 2021, 5:48:20 AM (5 years ago)
Author:
Carlos Garcia Campos
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):

Location:
trunk
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r287067 r287070  
     12021-12-15  Carlos Garcia Campos  <cgarcia@igalia.com>
     2
     3        [GTK][a11y] Register the wrappers tree when there's an event listener registered
     4        https://bugs.webkit.org/show_bug.cgi?id=234338
     5
     6        Reviewed by Joanmarie Diggs.
     7
     8        Connect to the atspi registry to get the list of registered event listeners. If there's any listener, we register
     9        the tree to ensure we can emit signals, but we only emit the signals for which there's a listener registered,
     10        except for the ones required by the atspi cache.
     11
     12        * accessibility/atspi/AccessibilityAtspi.cpp:
     13        (WebCore::AccessibilityAtspi::AccessibilityAtspi): Connect to the registry.
     14        (WebCore::AccessibilityAtspi::~AccessibilityAtspi): Disconnect registry signals.
     15        (WebCore::AccessibilityAtspi::registerTrees const): Register the trees of all root objects.
     16        (WebCore::AccessibilityAtspi::initializeRegistry): Get the list of listeners registered and connect to
     17        EventListenerRegistered signal.
     18        (WebCore::AccessibilityAtspi::addEventListener): Add an event listener to the list converting the detail to non
     19        camel case to be abel to compare it later on signal emission.
     20        (WebCore::eventIsSubtype): Helper to check if event type is subtype of another.
     21        (WebCore::AccessibilityAtspi::removeEventListener): Remove and events listener from the list.
     22        (WebCore::AccessibilityAtspi::shouldEmitSignal): Return whether the given signal should be emitted, checking the
     23        registered event listeners.
     24        (WebCore::AccessibilityAtspi::parentChanged): Do not emit the signal if the object hasn't been registered yet to
     25        avoid flooding during tree creation. We just register the object the object in that case which will update the
     26        cache because addAccessible is always called.
     27        (WebCore::AccessibilityAtspi::stateChanged): Return early if signal should not be emitted.
     28        (WebCore::AccessibilityAtspi::textChanged): Ditto.
     29        (WebCore::AccessibilityAtspi::textAttributesChanged): Ditto.
     30        (WebCore::AccessibilityAtspi::textCaretMoved): Ditto.
     31        (WebCore::AccessibilityAtspi::textSelectionChanged): Ditto.
     32        (WebCore::AccessibilityAtspi::valueChanged): Ditto.
     33        (WebCore::AccessibilityAtspi::selectionChanged): Ditto.
     34        * accessibility/atspi/AccessibilityAtspi.h:
     35        (WebCore::AccessibilityAtspi::hasEventListeners const):
     36        * accessibility/atspi/AccessibilityObjectAtspi.cpp:
     37        (WebCore::AccessibilityObjectAtspi::childAdded): Return early if object is already detached or ignored.
     38        (WebCore::AccessibilityObjectAtspi::childRemoved): Ditto.
     39        (WebCore::AccessibilityObjectAtspi::stateChanged): Do not return early if the object is not registered because
     40        we might end up emitting the signal.
     41        * accessibility/atspi/AccessibilityObjectSelectionAtspi.cpp:
     42        (WebCore::AccessibilityObjectAtspi::selectionChanged): Ditto.
     43        * accessibility/atspi/AccessibilityObjectTextAtspi.cpp:
     44        (WebCore::AccessibilityObjectAtspi::textInserted): Ditto.
     45        (WebCore::AccessibilityObjectAtspi::textDeleted): Ditto.
     46        (WebCore::AccessibilityObjectAtspi::selectionChanged): Ditto.
     47        (WebCore::AccessibilityObjectAtspi::textAttributesChanged): Ditto.
     48        * accessibility/atspi/AccessibilityObjectValueAtspi.cpp:
     49        (WebCore::AccessibilityObjectAtspi::valueChanged): Ditto.
     50        * accessibility/atspi/AccessibilityRootAtspi.cpp:
     51        (WebCore::AccessibilityRootAtspi::embedded): Register the tree if there are event listeners registered.
     52
    1532021-12-15  Youenn Fablet  <youenn@apple.com>
    254
  • trunk/Source/WebCore/accessibility/atspi/AccessibilityAtspi.cpp

    r287020 r287070  
    3030#include <wtf/SortedArrayMap.h>
    3131#include <wtf/UUID.h>
    32 #include <wtf/glib/GUniquePtr.h>
    3332
    3433namespace WebCore {
     
    4544            static_cast<GDBusConnectionFlags>(G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION),
    4645            nullptr, nullptr, &error.outPtr()));
    47         if (!m_connection)
     46
     47        if (m_connection)
     48            initializeRegistry();
     49        else
    4850            g_warning("Can't connect to a11y bus: %s", error->message);
    4951    });
     52}
     53
     54AccessibilityAtspi::~AccessibilityAtspi()
     55{
     56    if (m_registry)
     57        g_signal_handlers_disconnect_by_data(m_registry.get(), this);
     58}
     59
     60void AccessibilityAtspi::registerTrees() const
     61{
     62    RELEASE_ASSERT(!isMainThread());
     63    for (auto* rootObject : m_rootObjects.keys()) {
     64        if (!rootObject->isTreeRegistered())
     65            rootObject->registerTree();
     66    }
     67}
     68
     69void AccessibilityAtspi::initializeRegistry()
     70{
     71    RELEASE_ASSERT(!isMainThread());
     72    RELEASE_ASSERT(m_connection);
     73    GUniqueOutPtr<GError> error;
     74    m_registry = adoptGRef(g_dbus_proxy_new_sync(m_connection.get(), G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, nullptr,
     75        "org.a11y.atspi.Registry", "/org/a11y/atspi/registry", "org.a11y.atspi.Registry", nullptr, &error.outPtr()));
     76    if (!m_registry) {
     77        g_warning("Failed to connect to atspi registry: %s\n", error->message);
     78        return;
     79    }
     80
     81    g_signal_connect(m_registry.get(), "g-signal", G_CALLBACK(+[](GDBusProxy*, char*, char* signal, GVariant* parameters, AccessibilityAtspi* atspi) {
     82        const char* dbusName;
     83        const char* eventName;
     84        if (!g_strcmp0(signal, "EventListenerRegistered")) {
     85            g_variant_get(parameters, "(&s&s@as)", &dbusName, &eventName, nullptr);
     86            atspi->addEventListener(dbusName, eventName);
     87            atspi->registerTrees();
     88        } else if (!g_strcmp0(signal, "EventListenerDeregistered")) {
     89            g_variant_get(parameters, "(&s&s)", &dbusName, &eventName);
     90            atspi->removeEventListener(dbusName, eventName);
     91        }
     92    }), this);
     93
     94    GRefPtr<GVariant> result = adoptGRef(g_dbus_proxy_call_sync(m_registry.get(), "GetRegisteredEvents", nullptr, G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &error.outPtr()));
     95    if (!result) {
     96        g_warning("Failed to get atspi registered event listeners: %s\n", error->message);
     97        return;
     98    }
     99
     100    GRefPtr<GVariant> events;
     101    g_variant_get(result.get(), "(@a(ss))", &events.outPtr());
     102    GVariantIter iter;
     103    auto eventCount = g_variant_iter_init(&iter, events.get());
     104    const char* dbusName;
     105    const char* eventName;
     106    while (g_variant_iter_loop(&iter, "(&s&s)", &dbusName, &eventName))
     107        addEventListener(dbusName, eventName);
     108
     109    if (eventCount)
     110        registerTrees();
     111}
     112
     113static GUniquePtr<char*> eventConvertingDetailToNonCamelCase(const char* eventName)
     114{
     115    GUniquePtr<char*> event(g_strsplit(eventName, ":", 3));
     116    if (!event.get()[0] || !event.get()[1] || !event.get()[2] || !*event.get()[2])
     117        return event;
     118
     119    char* converted = static_cast<char*>(g_malloc(strlen(event.get()[2]) * 2 + 1));
     120    char* convertedPtr = converted;
     121    char* detailPtr = event.get()[2];
     122
     123    while (*detailPtr) {
     124        if (isASCIIUpper(*detailPtr)) {
     125            if (convertedPtr > converted)
     126                *convertedPtr++ = '-';
     127            *convertedPtr++ = toASCIILower(*detailPtr++);
     128        } else
     129            *convertedPtr++ = *detailPtr++;
     130    }
     131    *convertedPtr = '\0';
     132
     133    g_free(event.get()[2]);
     134    event.get()[2] = converted;
     135
     136    return event;
     137}
     138
     139void AccessibilityAtspi::addEventListener(const char* dbusName, const char* eventName)
     140{
     141    RELEASE_ASSERT(!isMainThread());
     142    auto& listeners = m_eventListeners.ensure(dbusName, [] {
     143        return Vector<GUniquePtr<char*>> { };
     144    }).iterator->value;
     145    listeners.append(eventConvertingDetailToNonCamelCase(eventName));
     146}
     147
     148static bool eventIsSubtype(char** needle, char** haystack)
     149{
     150    while (*haystack && **haystack) {
     151        if (g_strcmp0(*needle, *haystack))
     152            return false;
     153        needle++;
     154        haystack++;
     155    }
     156
     157    return true;
     158}
     159
     160void AccessibilityAtspi::removeEventListener(const char* dbusName, const char* eventName)
     161{
     162    RELEASE_ASSERT(!isMainThread());
     163    if (!eventName || !*eventName) {
     164        m_eventListeners.remove(dbusName);
     165        return;
     166    }
     167
     168    auto it = m_eventListeners.find(dbusName);
     169    if (it == m_eventListeners.end())
     170        return;
     171
     172    auto needle = eventConvertingDetailToNonCamelCase(eventName);
     173    it->value.removeAllMatching([&](const GUniquePtr<char*>& event) {
     174        return eventIsSubtype(needle.get(), event.get());
     175    });
     176
     177    if (it->value.isEmpty())
     178        m_eventListeners.remove(it);
     179}
     180
     181bool AccessibilityAtspi::shouldEmitSignal(const char* interface, const char* name, const char* detail)
     182{
     183    RELEASE_ASSERT(!isMainThread());
     184
     185    // Always emit signals if we couldn't connect to the registry.
     186    if (!m_registry)
     187        return true;
     188
     189    if (m_eventListeners.isEmpty())
     190        return false;
     191
     192    const char* needle[4] = { interface, name, detail, nullptr };
     193    for (const auto& listeners : m_eventListeners.values()) {
     194        auto result = listeners.findMatching([&](const GUniquePtr<char*>& event) {
     195            return eventIsSubtype(const_cast<char**>(needle), event.get());
     196        });
     197        if (result != notFound)
     198            return true;
     199    }
     200
     201    return false;
    50202}
    51203
     
    181333            return;
    182334
    183         // We call path here to ensure it happens before parentReference() in case objects are not registered yet.
    184         auto path = atspiObject->path();
    185         g_dbus_connection_emit_signal(m_connection.get(), nullptr, path.utf8().data(), "org.a11y.atspi.Event.Object", "PropertyChange",
     335        // Emit parentChanged only if the object is already registered, otherwise register the object,
     336        // without emitting the signal, because org.a11y.atspi.Cache.AddAccessible() will update the cache.
     337        if (atspiObject->registerObject())
     338            return;
     339
     340        g_dbus_connection_emit_signal(m_connection.get(), nullptr, atspiObject->path().utf8().data(), "org.a11y.atspi.Event.Object", "PropertyChange",
    186341            g_variant_new("(siiva{sv})", "accessible-parent", 0, 0, atspiObject->parentReference(), nullptr), nullptr);
    187342    });
     
    212367            return;
    213368
     369        if (!shouldEmitSignal("Object", "StateChanged", name.data()))
     370            return;
     371
    214372        g_dbus_connection_emit_signal(m_connection.get(), nullptr, atspiObject->path().utf8().data(), "org.a11y.atspi.Event.Object", "StateChanged",
    215373            g_variant_new("(siiva{sv})", name.data(), value, 0, g_variant_new_string("0"), nullptr), nullptr);
     
    224382            return;
    225383
     384        if (!shouldEmitSignal("Object", "TextChanged", changeType.data()))
     385            return;
     386
    226387        g_dbus_connection_emit_signal(m_connection.get(), nullptr, atspiObject->path().utf8().data(), "org.a11y.atspi.Event.Object", "TextChanged",
    227388            g_variant_new("(siiva{sv})", changeType.data(), offset, length, g_variant_new_string(text.data()), nullptr), nullptr);
     
    236397            return;
    237398
     399        if (!shouldEmitSignal("Object", "TextAttributesChanged"))
     400            return;
     401
    238402        g_dbus_connection_emit_signal(m_connection.get(), nullptr, atspiObject->path().utf8().data(), "org.a11y.atspi.Event.Object", "TextAttributesChanged",
    239403            g_variant_new("(siiva{sv})", "", 0, 0, g_variant_new_string(""), nullptr), nullptr);
     
    248412            return;
    249413
     414        if (!shouldEmitSignal("Object", "TextCaretMoved"))
     415            return;
     416
    250417        g_dbus_connection_emit_signal(m_connection.get(), nullptr, atspiObject->path().utf8().data(), "org.a11y.atspi.Event.Object", "TextCaretMoved",
    251418            g_variant_new("(siiva{sv})", "", caretOffset, 0, g_variant_new_string(""), nullptr), nullptr);
     
    260427            return;
    261428
     429        if (!shouldEmitSignal("Object", "TextSelectionChanged"))
     430            return;
     431
    262432        g_dbus_connection_emit_signal(m_connection.get(), nullptr, atspiObject->path().utf8().data(), "org.a11y.atspi.Event.Object", "TextSelectionChanged",
    263433            g_variant_new("(siiva{sv})", "", 0, 0, g_variant_new_string(""), nullptr), nullptr);
     
    272442            return;
    273443
     444        if (!shouldEmitSignal("Object", "PropertyChange", "accessible-value"))
     445            return;
     446
    274447        g_dbus_connection_emit_signal(m_connection.get(), nullptr, atspiObject->path().utf8().data(), "org.a11y.atspi.Event.Object", "PropertyChange",
    275448            g_variant_new("(siiva{sv})", "accessible-value", 0, 0, g_variant_new_double(value), nullptr), nullptr);
     
    282455    m_queue->dispatch([this, atspiObject = Ref { atspiObject }] {
    283456        if (!m_connection)
     457            return;
     458
     459        if (!shouldEmitSignal("Object", "SelectionChanged"))
    284460            return;
    285461
  • trunk/Source/WebCore/accessibility/atspi/AccessibilityAtspi.h

    r287020 r287070  
    2727#include <wtf/WorkQueue.h>
    2828#include <wtf/glib/GRefPtr.h>
     29#include <wtf/glib/GUniquePtr.h>
    2930
    3031typedef struct _GDBusConnection GDBusConnection;
    3132typedef struct _GDBusInterfaceInfo GDBusInterfaceInfo;
    3233typedef struct _GDBusInterfaceVTable GDBusInterfaceVTable;
     34typedef struct _GDBusProxy GDBusProxy;
    3335typedef struct _GVariant GVariant;
    3436
     
    4244public:
    4345    AccessibilityAtspi(const String&);
    44     ~AccessibilityAtspi() = default;
     46    ~AccessibilityAtspi();
    4547
    4648    WEBCORE_EXPORT RunLoop& runLoop() const;
     
    4850    const char* uniqueName() const;
    4951    GVariant* nullReference() const;
     52    bool hasEventListeners() const { return !m_eventListeners.isEmpty(); }
    5053
    5154    void registerRoot(AccessibilityRootAtspi&, Vector<std::pair<GDBusInterfaceInfo*, GDBusInterfaceVTable*>>&&, CompletionHandler<void(const String&)>&&);
     
    7578
    7679private:
     80    void registerTrees() const;
     81    void initializeRegistry();
     82    void addEventListener(const char* dbusName, const char* eventName);
     83    void removeEventListener(const char* dbusName, const char* eventName);
     84
    7785    void ensureCache();
    7886    void removeAccessible(AccessibilityObjectAtspi&);
     87
     88    bool shouldEmitSignal(const char* interface, const char* name, const char* detail = "");
    7989
    8090    static GDBusInterfaceVTable s_cacheFunctions;
     
    8292    Ref<WorkQueue> m_queue;
    8393    GRefPtr<GDBusConnection> m_connection;
     94    GRefPtr<GDBusProxy> m_registry;
     95    HashMap<CString, Vector<GUniquePtr<char*>>> m_eventListeners;
    8496    HashMap<AccessibilityRootAtspi*, Vector<unsigned, 2>> m_rootObjects;
    8597    HashMap<AccessibilityObjectAtspi*, Vector<unsigned, 20>> m_atspiObjects;
  • trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.cpp

    r287020 r287070  
    12141214        return;
    12151215
     1216    if (!m_coreObject || m_coreObject->accessibilityIsIgnored())
     1217        return;
     1218
    12161219    m_root.atspi().childrenChanged(*this, child, AccessibilityAtspi::ChildrenChanged::Added);
    12171220}
     
    12231226        return;
    12241227
     1228    if (!m_coreObject || m_coreObject->accessibilityIsIgnored())
     1229        return;
     1230
    12251231    m_root.atspi().childrenChanged(*this, child, AccessibilityAtspi::ChildrenChanged::Removed);
    12261232}
     
    12291235{
    12301236    RELEASE_ASSERT(isMainThread());
    1231     if (!m_isRegistered.load())
    1232         return;
    1233 
    12341237    m_root.atspi().stateChanged(*this, name, value);
    12351238}
  • trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectSelectionAtspi.cpp

    r286767 r287070  
    225225{
    226226    RELEASE_ASSERT(isMainThread());
    227     if (!m_isRegistered.load())
    228         return;
    229 
    230227    m_root.atspi().selectionChanged(*this);
    231228}
  • trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectTextAtspi.cpp

    r286767 r287070  
    349349{
    350350    RELEASE_ASSERT(isMainThread());
    351     if (!m_isRegistered.load())
    352         return;
    353 
    354351    if (!m_interfaces.contains(Interface::Text))
    355352        return;
     
    368365{
    369366    RELEASE_ASSERT(isMainThread());
    370     if (!m_isRegistered.load())
    371         return;
    372 
    373367    if (!m_interfaces.contains(Interface::Text))
    374368        return;
     
    752746{
    753747    RELEASE_ASSERT(isMainThread());
    754     if (!m_isRegistered.load())
    755         return;
    756 
    757748    if (!m_interfaces.contains(Interface::Text))
    758749        return;
     
    944935{
    945936    RELEASE_ASSERT(isMainThread());
    946     if (!m_isRegistered.load())
    947         return;
    948 
    949937    if (!m_interfaces.contains(Interface::Text))
    950938        return;
  • trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectValueAtspi.cpp

    r286767 r287070  
    135135{
    136136    RELEASE_ASSERT(isMainThread());
    137     if (!m_isRegistered.load())
    138         return;
    139 
    140137    m_root.atspi().valueChanged(*this, value);
    141138}
  • trunk/Source/WebCore/accessibility/atspi/AccessibilityRootAtspi.cpp

    r287020 r287070  
    224224    m_parentUniqueName = parentUniqueName;
    225225    m_parentPath = parentPath;
     226    if (!m_isTreeRegistered.load() && m_atspi.hasEventListeners())
     227        registerTree();
    226228}
    227229
  • trunk/Tools/ChangeLog

    r287060 r287070  
     12021-12-15  Carlos Garcia Campos  <cgarcia@igalia.com>
     2
     3        [GTK][a11y] Register the wrappers tree when there's an event listener registered
     4        https://bugs.webkit.org/show_bug.cgi?id=234338
     5
     6        Reviewed by Joanmarie Diggs.
     7
     8        Add unit test to check the tree is created when there's a listener connected.
     9
     10        * TestWebKitAPI/Tests/WebKitGtk/TestWebKitAccessibility.cpp:
     11        (AccessibilityTest::findTestApplication): Iterate the applications list backwards since the test program is
     12        usually the last one in the list.
     13        (AccessibilityTest::accessibleApplicationIsTestProgram): Helper to check if a given accessible belongs to the
     14        test program.
     15        (AccessibilityTest::startEventMonitor): Support for listening to events associated to the test program, not to a
     16        particular accessible.
     17        (testAccessibleEventListener):
     18        (beforeAll):
     19
    1202021-12-14  Don Olmstead  <don.olmstead@sony.com>
    221
  • trunk/Tools/TestWebKitAPI/Tests/WebKitGtk/TestWebKitAccessibility.cpp

    r286416 r287070  
    5858        int childCount = atspi_accessible_get_child_count(desktop.get(), nullptr);
    5959        Test::addLogFatalFlag(G_LOG_LEVEL_WARNING);
    60         for (int i = 0; i < childCount; ++i) {
     60        for (int i = childCount - 1; i >= 0; --i) {
    6161            GRefPtr<AtspiAccessible> current = adoptGRef(atspi_accessible_get_child_at_index(desktop.get(), i, nullptr));
    62             if (!g_strcmp0(atspi_accessible_get_name(current.get(), nullptr), "TestWebKitAccessibility"))
     62            GUniquePtr<char> name(atspi_accessible_get_name(current.get(), nullptr));
     63            if (!g_strcmp0(name.get(), "TestWebKitAccessibility"))
    6364                return current;
    6465        }
     
    107108    }
    108109
     110    static bool accessibleApplicationIsTestProgram(AtspiAccessible* accessible)
     111    {
     112        GRefPtr<AtspiAccessible> application = adoptGRef(atspi_accessible_get_application(accessible, nullptr));
     113        GUniquePtr<char> applicationName(atspi_accessible_get_name(application.get(), nullptr));
     114        return !g_strcmp0(applicationName.get(), "TestWebKitAccessibility");
     115    }
     116
    109117    void startEventMonitor(AtspiAccessible* source, Vector<CString>&& events)
    110118    {
     
    113121        m_eventMonitor.listener = adoptGRef(atspi_event_listener_new([](AtspiEvent* event, gpointer userData) {
    114122            auto* test = static_cast<AccessibilityTest*>(userData);
    115             if (event->source == test->m_eventMonitor.source)
     123            if ((test->m_eventMonitor.source && event->source == test->m_eventMonitor.source)
     124                || (!test->m_eventMonitor.source && accessibleApplicationIsTestProgram(event->source))) {
    116125                test->m_eventMonitor.events.append(static_cast<AtspiEvent*>(g_boxed_copy(ATSPI_TYPE_EVENT, event)));
     126            }
    117127        }, this, nullptr));
    118128
     
    810820    g_assert_false(AccessibilityTest::isSelected(option2.get()));
    811821#endif
     822}
     823
     824static void testAccessibleEventListener(AccessibilityTest* test, gconstpointer)
     825{
     826    test->startEventMonitor(nullptr, { "object:state-changed:focused" });
     827    test->showInWindow();
     828    test->loadHtml(
     829        "<html>"
     830        "  <body>"
     831        "    <input id='entry' type='text'/>"
     832        "  </body>"
     833        "</html>",
     834        nullptr);
     835    test->waitUntilLoadFinished();
     836
     837    test->runJavaScriptAndWaitUntilFinished("document.getElementById('entry').focus();", nullptr);
     838
     839    auto events = test->stopEventMonitor(1);
     840    g_assert_cmpuint(events.size(), ==, 1);
     841    g_assert_cmpstr(events[0]->type, ==, "object:state-changed:focused");
     842    auto* entry = events[0]->source;
     843    g_assert_true(ATSPI_IS_ACCESSIBLE(entry));
     844    g_assert_cmpint(atspi_accessible_get_role(entry, nullptr), ==, ATSPI_ROLE_ENTRY);
     845
     846    auto panel = adoptGRef(atspi_accessible_get_parent(entry, nullptr));
     847    g_assert_true(ATSPI_IS_ACCESSIBLE(panel.get()));
     848    g_assert_cmpint(atspi_accessible_get_role(panel.get(), nullptr), ==, ATSPI_ROLE_PANEL);
     849
     850    auto documentWeb = adoptGRef(atspi_accessible_get_parent(panel.get(), nullptr));
     851    g_assert_true(ATSPI_IS_ACCESSIBLE(documentWeb.get()));
     852    g_assert_cmpint(atspi_accessible_get_role(documentWeb.get(), nullptr), ==, ATSPI_ROLE_DOCUMENT_WEB);
     853
     854    auto scrollView = adoptGRef(atspi_accessible_get_parent(documentWeb.get(), nullptr));
     855    g_assert_true(ATSPI_IS_ACCESSIBLE(scrollView.get()));
     856    g_assert_cmpint(atspi_accessible_get_role(scrollView.get(), nullptr), ==, ATSPI_ROLE_SCROLL_PANE);
     857
     858    auto rootObject = adoptGRef(atspi_accessible_get_parent(scrollView.get(), nullptr));
     859    g_assert_true(ATSPI_IS_ACCESSIBLE(rootObject.get()));
     860    g_assert_cmpint(atspi_accessible_get_role(rootObject.get(), nullptr), ==, ATSPI_ROLE_FILLER);
    812861}
    813862
     
    27532802    AccessibilityTest::add("WebKitAccessibility", "accessible/state", testAccessibleState);
    27542803    AccessibilityTest::add("WebKitAccessibility", "accessible/state-changed", testAccessibleStateChanged);
     2804    AccessibilityTest::add("WebKitAccessibility", "accessible/event-listener", testAccessibleEventListener);
    27552805    AccessibilityTest::add("WebKitAccessibility", "component/hit-test", testComponentHitTest);
    27562806#ifdef ATSPI_SCROLLTYPE_COUNT
Note: See TracChangeset for help on using the changeset viewer.