Title: [285996] trunk
Revision
285996
Author
[email protected]
Date
2021-11-18 03:46:10 -0800 (Thu, 18 Nov 2021)

Log Message

[GTK][a11y] Add initial WTR accessibility support when building with ATSPI
https://bugs.webkit.org/show_bug.cgi?id=230259

Reviewed by Adrian Perez de Castro.

Source/WebCore:

Export symbols required by WTR.

* accessibility/atspi/AccessibilityAtspi.cpp:
(WebCore::AccessibilityAtspi::runLoop const):
* accessibility/atspi/AccessibilityAtspi.h:
* accessibility/atspi/AccessibilityObjectAtspi.h:
* accessibility/atspi/AccessibilityRootAtspi.cpp:
(WebCore::AccessibilityRootAtspi::focusedObject const):
* accessibility/atspi/AccessibilityRootAtspi.h:

Source/WebKit:

In case of ATSPI we need the web page accessibility object to get the root and focused object wrappers.

* WebProcess/InjectedBundle/API/c/WKBundlePage.cpp:
(WKAccessibilityRootObject):
(WKAccessibilityFocusedObject):

Tools:

* WebKitTestRunner/InjectedBundle/AccessibilityController.h:
* WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h:
* WebKitTestRunner/InjectedBundle/atspi/AccessibilityControllerAtspi.cpp:
(WTR::findAccessibleObjectById):
(WTR::AccessibilityController::accessibleElementById):
(WTR::AccessibilityController::platformName):
(WTR::AccessibilityController::axRunLoop):
(WTR::AccessibilityController::executeOnAXThreadAndWait):
(WTR::AccessibilityController::executeOnAXThread):
(WTR::AccessibilityController::executeOnMainThread):
* WebKitTestRunner/InjectedBundle/atspi/AccessibilityUIElementAtspi.cpp:
(WTR::AccessibilityUIElement::AccessibilityUIElement):
(WTR::AccessibilityUIElement::isEqual):
(WTR::AccessibilityUIElement::childrenCount):
(WTR::AccessibilityUIElement::elementAtPoint):
(WTR::AccessibilityUIElement::childAtIndex):
(WTR::elementForRelationAtIndex):
(WTR::AccessibilityUIElement::ariaOwnsElementAtIndex):
(WTR::AccessibilityUIElement::ariaFlowToElementAtIndex):
(WTR::AccessibilityUIElement::ariaControlsElementAtIndex):
(WTR::AccessibilityUIElement::titleUIElement):
(WTR::AccessibilityUIElement::parentElement):
(WTR::attributesOfElement):
(WTR::attributesOfElements):
(WTR::AccessibilityUIElement::attributesOfChildren):
(WTR::AccessibilityUIElement::allAttributes):
(WTR::AccessibilityUIElement::stringAttributeValue):
(WTR::AccessibilityUIElement::numberAttributeValue):
(WTR::checkElementState):
(WTR::AccessibilityUIElement::boolAttributeValue):
(WTR::AccessibilityUIElement::isAttributeSupported):
(WTR::roleValueToString):
(WTR::AccessibilityUIElement::role):
(WTR::AccessibilityUIElement::roleDescription):
(WTR::AccessibilityUIElement::computedRoleString):
(WTR::AccessibilityUIElement::title):
(WTR::AccessibilityUIElement::description):
(WTR::AccessibilityUIElement::orientation const):
(WTR::AccessibilityUIElement::stringValue):
(WTR::AccessibilityUIElement::helpText const):
(WTR::AccessibilityUIElement::x):
(WTR::AccessibilityUIElement::y):
(WTR::AccessibilityUIElement::width):
(WTR::AccessibilityUIElement::height):
(WTR::AccessibilityUIElement::clickPointX):
(WTR::AccessibilityUIElement::clickPointY):
(WTR::AccessibilityUIElement::isEnabled):
(WTR::AccessibilityUIElement::isRequired const):
(WTR::AccessibilityUIElement::isFocused const):
(WTR::AccessibilityUIElement::isSelected const):
(WTR::AccessibilityUIElement::isSelectedOptionActive const):
(WTR::AccessibilityUIElement::isExpanded const):
(WTR::AccessibilityUIElement::isChecked const):
(WTR::AccessibilityUIElement::isIndeterminate const):
(WTR::AccessibilityUIElement::hierarchicalLevel const):
(WTR::AccessibilityUIElement::ariaIsGrabbed const):
(WTR::AccessibilityUIElement::ariaDropEffects const):
(WTR::AccessibilityUIElement::lineForIndex):
(WTR::AccessibilityUIElement::rangeForLine):
(WTR::AccessibilityUIElement::boundsForRange):
(WTR::AccessibilityUIElement::stringForRange):
(WTR::AccessibilityUIElement::attributedStringForRange):
(WTR::AccessibilityUIElement::selectedTextRange):
(WTR::AccessibilityUIElement::setSelectedTextRange):
(WTR::AccessibilityUIElement::isFocusable const):
(WTR::AccessibilityUIElement::isSelectable const):
(WTR::AccessibilityUIElement::isMultiSelectable const):
(WTR::AccessibilityUIElement::isVisible const):
(WTR::AccessibilityUIElement::isOffScreen const):
(WTR::AccessibilityUIElement::isCollapsed const):
(WTR::AccessibilityUIElement::isSingleLine const):
(WTR::AccessibilityUIElement::isMultiLine const):
(WTR::AccessibilityUIElement::hasPopup const):
(WTR::AccessibilityUIElement::scrollToMakeVisible):
(WTR::AccessibilityUIElement::scrollToGlobalPoint):
(WTR::stringAtOffset):
(WTR::AccessibilityUIElement::characterAtOffset):
(WTR::AccessibilityUIElement::wordAtOffset):
(WTR::AccessibilityUIElement::lineAtOffset):
(WTR::AccessibilityUIElement::sentenceAtOffset):
* WebKitTestRunner/gtk/main.cpp:
(main):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (285995 => 285996)


--- trunk/Source/WebCore/ChangeLog	2021-11-18 11:19:38 UTC (rev 285995)
+++ trunk/Source/WebCore/ChangeLog	2021-11-18 11:46:10 UTC (rev 285996)
@@ -1,3 +1,20 @@
+2021-10-25  Carlos Garcia Campos  <[email protected]>
+
+        [GTK][a11y] Add initial WTR accessibility support when building with ATSPI
+        https://bugs.webkit.org/show_bug.cgi?id=230259
+
+        Reviewed by Adrian Perez de Castro.
+
+        Export symbols required by WTR.
+
+        * accessibility/atspi/AccessibilityAtspi.cpp:
+        (WebCore::AccessibilityAtspi::runLoop const):
+        * accessibility/atspi/AccessibilityAtspi.h:
+        * accessibility/atspi/AccessibilityObjectAtspi.h:
+        * accessibility/atspi/AccessibilityRootAtspi.cpp:
+        (WebCore::AccessibilityRootAtspi::focusedObject const):
+        * accessibility/atspi/AccessibilityRootAtspi.h:
+
 2021-11-18  Carlos Garcia Campos  <[email protected]>
 
         Unreviewed. Fix GTK build with ATSPI enaled after r285904

Modified: trunk/Source/WebCore/accessibility/atspi/AccessibilityAtspi.cpp (285995 => 285996)


--- trunk/Source/WebCore/accessibility/atspi/AccessibilityAtspi.cpp	2021-11-18 11:19:38 UTC (rev 285995)
+++ trunk/Source/WebCore/accessibility/atspi/AccessibilityAtspi.cpp	2021-11-18 11:46:10 UTC (rev 285996)
@@ -48,6 +48,11 @@
     });
 }
 
+RunLoop& AccessibilityAtspi::runLoop() const
+{
+    return m_queue->runLoop();
+}
+
 const char* AccessibilityAtspi::uniqueName() const
 {
     RELEASE_ASSERT(!isMainThread());

Modified: trunk/Source/WebCore/accessibility/atspi/AccessibilityAtspi.h (285995 => 285996)


--- trunk/Source/WebCore/accessibility/atspi/AccessibilityAtspi.h	2021-11-18 11:19:38 UTC (rev 285995)
+++ trunk/Source/WebCore/accessibility/atspi/AccessibilityAtspi.h	2021-11-18 11:46:10 UTC (rev 285996)
@@ -43,6 +43,8 @@
     AccessibilityAtspi(const String&);
     ~AccessibilityAtspi() = default;
 
+    WEBCORE_EXPORT RunLoop& runLoop() const;
+
     const char* uniqueName() const;
     GVariant* nullReference() const;
 

Modified: trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.h (285995 => 285996)


--- trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.h	2021-11-18 11:19:38 UTC (rev 285995)
+++ trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.h	2021-11-18 11:46:10 UTC (rev 285996)
@@ -50,10 +50,10 @@
     const OptionSet<Interface>& interfaces() const { return m_interfaces; }
 
     void setRoot(AccessibilityRootAtspi*);
-    AccessibilityRootAtspi* root() const;
+    WEBCORE_EXPORT AccessibilityRootAtspi* root() const;
     void setParent(std::optional<AccessibilityObjectAtspi*>);
-    std::optional<AccessibilityObjectAtspi*> parent() const;
-    void updateBackingStore();
+    WEBCORE_EXPORT std::optional<AccessibilityObjectAtspi*> parent() const;
+    WEBCORE_EXPORT void updateBackingStore();
 
     void attach(AXCoreObject*);
     void detach();
@@ -66,24 +66,24 @@
     GVariant* reference();
     void serialize(GVariantBuilder*) const;
 
-    String id() const;
-    CString name() const;
-    CString description() const;
-    unsigned role() const;
-    unsigned childCount() const;
-    Vector<RefPtr<AccessibilityObjectAtspi>> children() const;
-    AccessibilityObjectAtspi* childAt(unsigned) const;
-    uint64_t state() const;
+    WEBCORE_EXPORT String id() const;
+    WEBCORE_EXPORT CString name() const;
+    WEBCORE_EXPORT CString description() const;
+    WEBCORE_EXPORT unsigned role() const;
+    WEBCORE_EXPORT unsigned childCount() const;
+    WEBCORE_EXPORT Vector<RefPtr<AccessibilityObjectAtspi>> children() const;
+    WEBCORE_EXPORT AccessibilityObjectAtspi* childAt(unsigned) const;
+    WEBCORE_EXPORT uint64_t state() const;
     void stateChanged(const char*, bool);
-    HashMap<String, String> attributes() const;
-    HashMap<uint32_t, Vector<RefPtr<AccessibilityObjectAtspi>>> relationMap() const;
+    WEBCORE_EXPORT HashMap<String, String> attributes() const;
+    WEBCORE_EXPORT HashMap<uint32_t, Vector<RefPtr<AccessibilityObjectAtspi>>> relationMap() const;
 
-    AccessibilityObjectAtspi* hitTest(const IntPoint&, uint32_t) const;
-    IntRect elementRect(uint32_t) const;
-    void scrollToMakeVisible(uint32_t) const;
-    void scrollToPoint(const IntPoint&, uint32_t) const;
+    WEBCORE_EXPORT AccessibilityObjectAtspi* hitTest(const IntPoint&, uint32_t) const;
+    WEBCORE_EXPORT IntRect elementRect(uint32_t) const;
+    WEBCORE_EXPORT void scrollToMakeVisible(uint32_t) const;
+    WEBCORE_EXPORT void scrollToPoint(const IntPoint&, uint32_t) const;
 
-    String text() const;
+    WEBCORE_EXPORT String text() const;
     enum class TextGranularity {
         Character,
         WordStart,
@@ -94,16 +94,16 @@
         LineEnd,
         Paragraph
     };
-    IntPoint boundaryOffset(unsigned, TextGranularity) const;
-    IntRect boundsForRange(unsigned, unsigned, uint32_t) const;
+    WEBCORE_EXPORT IntPoint boundaryOffset(unsigned, TextGranularity) const;
+    WEBCORE_EXPORT IntRect boundsForRange(unsigned, unsigned, uint32_t) const;
     struct TextAttributes {
         HashMap<String, String> attributes;
         int startOffset;
         int endOffset;
     };
-    TextAttributes textAttributes(std::optional<unsigned> = std::nullopt, bool = false) const;
-    IntPoint selectedRange() const;
-    void setSelectedRange(unsigned, unsigned);
+    WEBCORE_EXPORT TextAttributes textAttributes(std::optional<unsigned> = std::nullopt, bool = false) const;
+    WEBCORE_EXPORT IntPoint selectedRange() const;
+    WEBCORE_EXPORT void setSelectedRange(unsigned, unsigned);
     void textInserted(const String&, const VisiblePosition&);
     void textDeleted(const String&, const VisiblePosition&);
     void textAttributesChanged();

Modified: trunk/Source/WebCore/accessibility/atspi/AccessibilityRootAtspi.cpp (285995 => 285996)


--- trunk/Source/WebCore/accessibility/atspi/AccessibilityRootAtspi.cpp	2021-11-18 11:19:38 UTC (rev 285995)
+++ trunk/Source/WebCore/accessibility/atspi/AccessibilityRootAtspi.cpp	2021-11-18 11:46:10 UTC (rev 285996)
@@ -25,6 +25,7 @@
 #include "AccessibilityAtspiEnums.h"
 #include "AccessibilityAtspiInterfaces.h"
 #include "Document.h"
+#include "FocusController.h"
 #include "Frame.h"
 #include "Page.h"
 #include <glib/gi18n-lib.h>
@@ -224,6 +225,36 @@
     return wrapper;
 }
 
+AccessibilityObjectAtspi* AccessibilityRootAtspi::focusedObject() const
+{
+    RELEASE_ASSERT(isMainThread());
+    if (!AXObjectCache::accessibilityEnabled())
+        AXObjectCache::enableAccessibility();
+
+    if (!m_page)
+        return nullptr;
+
+    auto* focusedDocument = m_page->focusController().focusedOrMainFrame().document();
+    if (!focusedDocument)
+        return nullptr;
+
+    auto* cache = focusedDocument->axObjectCache();
+    if (!cache)
+        return nullptr;
+
+    auto* focusedObject = cache->focusedObjectForPage(m_page.get());
+    if (!focusedObject)
+        return nullptr;
+
+    auto* wrapper = focusedObject->wrapper();
+    if (!wrapper)
+        return nullptr;
+
+    wrapper->setRoot(const_cast<AccessibilityRootAtspi*>(this));
+
+    return wrapper;
+}
+
 void AccessibilityRootAtspi::serialize(GVariantBuilder* builder) const
 {
     RELEASE_ASSERT(!isMainThread());

Modified: trunk/Source/WebCore/accessibility/atspi/AccessibilityRootAtspi.h (285995 => 285996)


--- trunk/Source/WebCore/accessibility/atspi/AccessibilityRootAtspi.h	2021-11-18 11:19:38 UTC (rev 285995)
+++ trunk/Source/WebCore/accessibility/atspi/AccessibilityRootAtspi.h	2021-11-18 11:46:10 UTC (rev 285996)
@@ -49,6 +49,7 @@
     GVariant* applicationReference() const;
     AccessibilityAtspi& atspi() const { return m_atspi; }
     AccessibilityObjectAtspi* child() const;
+    AccessibilityObjectAtspi* focusedObject() const;
 
     void serialize(GVariantBuilder*) const;
 

Modified: trunk/Source/WebKit/ChangeLog (285995 => 285996)


--- trunk/Source/WebKit/ChangeLog	2021-11-18 11:19:38 UTC (rev 285995)
+++ trunk/Source/WebKit/ChangeLog	2021-11-18 11:46:10 UTC (rev 285996)
@@ -1,3 +1,16 @@
+2021-10-25  Carlos Garcia Campos  <[email protected]>
+
+        [GTK][a11y] Add initial WTR accessibility support when building with ATSPI
+        https://bugs.webkit.org/show_bug.cgi?id=230259
+
+        Reviewed by Adrian Perez de Castro.
+
+        In case of ATSPI we need the web page accessibility object to get the root and focused object wrappers.
+
+        * WebProcess/InjectedBundle/API/c/WKBundlePage.cpp:
+        (WKAccessibilityRootObject):
+        (WKAccessibilityFocusedObject):
+
 2021-11-18  Youenn Fablet  <[email protected]>
 
         MediaPlayerAVFoundation should support rvfc

Modified: trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.cpp (285995 => 285996)


--- trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.cpp	2021-11-18 11:19:38 UTC (rev 285995)
+++ trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.cpp	2021-11-18 11:46:10 UTC (rev 285996)
@@ -246,7 +246,11 @@
 #if ENABLE(ACCESSIBILITY)
     if (!pageRef)
         return 0;
-    
+
+#if USE(ATSPI)
+    return WebKit::toImpl(pageRef)->accessibilityRootObject().child();
+#endif
+
     WebCore::Page* page = WebKit::toImpl(pageRef)->corePage();
     if (!page)
         return 0;
@@ -274,6 +278,10 @@
     if (!pageRef)
         return 0;
 
+#if USE(ATSPI)
+    return WebKit::toImpl(pageRef)->accessibilityRootObject().focusedObject();
+#endif
+
     WebCore::Page* page = WebKit::toImpl(pageRef)->corePage();
     if (!page)
         return 0;

Modified: trunk/Tools/ChangeLog (285995 => 285996)


--- trunk/Tools/ChangeLog	2021-11-18 11:19:38 UTC (rev 285995)
+++ trunk/Tools/ChangeLog	2021-11-18 11:46:10 UTC (rev 285996)
@@ -1,3 +1,93 @@
+2021-10-25  Carlos Garcia Campos  <[email protected]>
+
+        [GTK][a11y] Add initial WTR accessibility support when building with ATSPI
+        https://bugs.webkit.org/show_bug.cgi?id=230259
+
+        Reviewed by Adrian Perez de Castro.
+
+        * WebKitTestRunner/InjectedBundle/AccessibilityController.h:
+        * WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h:
+        * WebKitTestRunner/InjectedBundle/atspi/AccessibilityControllerAtspi.cpp:
+        (WTR::findAccessibleObjectById):
+        (WTR::AccessibilityController::accessibleElementById):
+        (WTR::AccessibilityController::platformName):
+        (WTR::AccessibilityController::axRunLoop):
+        (WTR::AccessibilityController::executeOnAXThreadAndWait):
+        (WTR::AccessibilityController::executeOnAXThread):
+        (WTR::AccessibilityController::executeOnMainThread):
+        * WebKitTestRunner/InjectedBundle/atspi/AccessibilityUIElementAtspi.cpp:
+        (WTR::AccessibilityUIElement::AccessibilityUIElement):
+        (WTR::AccessibilityUIElement::isEqual):
+        (WTR::AccessibilityUIElement::childrenCount):
+        (WTR::AccessibilityUIElement::elementAtPoint):
+        (WTR::AccessibilityUIElement::childAtIndex):
+        (WTR::elementForRelationAtIndex):
+        (WTR::AccessibilityUIElement::ariaOwnsElementAtIndex):
+        (WTR::AccessibilityUIElement::ariaFlowToElementAtIndex):
+        (WTR::AccessibilityUIElement::ariaControlsElementAtIndex):
+        (WTR::AccessibilityUIElement::titleUIElement):
+        (WTR::AccessibilityUIElement::parentElement):
+        (WTR::attributesOfElement):
+        (WTR::attributesOfElements):
+        (WTR::AccessibilityUIElement::attributesOfChildren):
+        (WTR::AccessibilityUIElement::allAttributes):
+        (WTR::AccessibilityUIElement::stringAttributeValue):
+        (WTR::AccessibilityUIElement::numberAttributeValue):
+        (WTR::checkElementState):
+        (WTR::AccessibilityUIElement::boolAttributeValue):
+        (WTR::AccessibilityUIElement::isAttributeSupported):
+        (WTR::roleValueToString):
+        (WTR::AccessibilityUIElement::role):
+        (WTR::AccessibilityUIElement::roleDescription):
+        (WTR::AccessibilityUIElement::computedRoleString):
+        (WTR::AccessibilityUIElement::title):
+        (WTR::AccessibilityUIElement::description):
+        (WTR::AccessibilityUIElement::orientation const):
+        (WTR::AccessibilityUIElement::stringValue):
+        (WTR::AccessibilityUIElement::helpText const):
+        (WTR::AccessibilityUIElement::x):
+        (WTR::AccessibilityUIElement::y):
+        (WTR::AccessibilityUIElement::width):
+        (WTR::AccessibilityUIElement::height):
+        (WTR::AccessibilityUIElement::clickPointX):
+        (WTR::AccessibilityUIElement::clickPointY):
+        (WTR::AccessibilityUIElement::isEnabled):
+        (WTR::AccessibilityUIElement::isRequired const):
+        (WTR::AccessibilityUIElement::isFocused const):
+        (WTR::AccessibilityUIElement::isSelected const):
+        (WTR::AccessibilityUIElement::isSelectedOptionActive const):
+        (WTR::AccessibilityUIElement::isExpanded const):
+        (WTR::AccessibilityUIElement::isChecked const):
+        (WTR::AccessibilityUIElement::isIndeterminate const):
+        (WTR::AccessibilityUIElement::hierarchicalLevel const):
+        (WTR::AccessibilityUIElement::ariaIsGrabbed const):
+        (WTR::AccessibilityUIElement::ariaDropEffects const):
+        (WTR::AccessibilityUIElement::lineForIndex):
+        (WTR::AccessibilityUIElement::rangeForLine):
+        (WTR::AccessibilityUIElement::boundsForRange):
+        (WTR::AccessibilityUIElement::stringForRange):
+        (WTR::AccessibilityUIElement::attributedStringForRange):
+        (WTR::AccessibilityUIElement::selectedTextRange):
+        (WTR::AccessibilityUIElement::setSelectedTextRange):
+        (WTR::AccessibilityUIElement::isFocusable const):
+        (WTR::AccessibilityUIElement::isSelectable const):
+        (WTR::AccessibilityUIElement::isMultiSelectable const):
+        (WTR::AccessibilityUIElement::isVisible const):
+        (WTR::AccessibilityUIElement::isOffScreen const):
+        (WTR::AccessibilityUIElement::isCollapsed const):
+        (WTR::AccessibilityUIElement::isSingleLine const):
+        (WTR::AccessibilityUIElement::isMultiLine const):
+        (WTR::AccessibilityUIElement::hasPopup const):
+        (WTR::AccessibilityUIElement::scrollToMakeVisible):
+        (WTR::AccessibilityUIElement::scrollToGlobalPoint):
+        (WTR::stringAtOffset):
+        (WTR::AccessibilityUIElement::characterAtOffset):
+        (WTR::AccessibilityUIElement::wordAtOffset):
+        (WTR::AccessibilityUIElement::lineAtOffset):
+        (WTR::AccessibilityUIElement::sentenceAtOffset):
+        * WebKitTestRunner/gtk/main.cpp:
+        (main):
+
 2021-11-18  Youenn Fablet  <[email protected]>
 
         MediaPlayerAVFoundation should support rvfc

Modified: trunk/Tools/WebKitTestRunner/InjectedBundle/AccessibilityController.h (285995 => 285996)


--- trunk/Tools/WebKitTestRunner/InjectedBundle/AccessibilityController.h	2021-11-18 11:19:38 UTC (rev 285995)
+++ trunk/Tools/WebKitTestRunner/InjectedBundle/AccessibilityController.h	2021-11-18 11:46:10 UTC (rev 285996)
@@ -34,6 +34,7 @@
 #if PLATFORM(COCOA)
 #include <wtf/RetainPtr.h>
 #endif
+#include <wtf/RunLoop.h>
 #include <wtf/Threading.h>
 
 #if USE(ATK)
@@ -68,7 +69,7 @@
     RefPtr<AccessibilityUIElement> elementAtPoint(int x, int y);
     RefPtr<AccessibilityUIElement> accessibleElementById(JSStringRef idAttribute);
 
-#if PLATFORM(COCOA)
+#if PLATFORM(COCOA) || USE(ATSPI)
     void executeOnAXThreadAndWait(Function<void()>&&);
     void executeOnAXThread(Function<void()>&&);
     void executeOnMainThread(Function<void()>&&);
@@ -103,6 +104,10 @@
 #if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
     void updateIsolatedTreeMode();
 
+#if USE(ATSPI)
+    RunLoop& axRunLoop();
+#endif
+
 #if PLATFORM(COCOA)
     void spinMainRunLoop() const;
     // _AXUIElementUseSecondaryAXThread and _AXUIElementRequestServicedBySecondaryAXThread
@@ -112,7 +117,10 @@
     bool m_useMockAXThread { false };
 #endif
     bool m_accessibilityIsolatedTreeMode { false };
+#if USE(ATSPI)
+    RunLoop* m_axRunLoop { nullptr };
 #endif
+#endif
 };
 
 #if PLATFORM(COCOA)

Modified: trunk/Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h (285995 => 285996)


--- trunk/Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h	2021-11-18 11:19:38 UTC (rev 285995)
+++ trunk/Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h	2021-11-18 11:46:10 UTC (rev 285996)
@@ -430,6 +430,7 @@
 #if USE(ATK)
     RefPtr<AccessibilityNotificationHandler> m_notificationHandler;
 #elif USE(ATSPI)
+    static RefPtr<AccessibilityController> s_controller;
     RefPtr<WebCore::AccessibilityObjectAtspi> m_element;
 #endif
 #endif

Modified: trunk/Tools/WebKitTestRunner/InjectedBundle/atspi/AccessibilityControllerAtspi.cpp (285995 => 285996)


--- trunk/Tools/WebKitTestRunner/InjectedBundle/atspi/AccessibilityControllerAtspi.cpp	2021-11-18 11:19:38 UTC (rev 285995)
+++ trunk/Tools/WebKitTestRunner/InjectedBundle/atspi/AccessibilityControllerAtspi.cpp	2021-11-18 11:46:10 UTC (rev 285996)
@@ -32,7 +32,9 @@
 #include "AccessibilityUIElement.h"
 #include "InjectedBundle.h"
 #include "InjectedBundlePage.h"
+#include "StringFunctions.h"
 #include <WebCore/AccessibilityObjectAtspi.h>
+#include <WebCore/AccessibilityRootAtspi.h>
 #include <WebKit/WKBundlePagePrivate.h>
 
 namespace WTR {
@@ -41,14 +43,44 @@
 {
 }
 
+static WebCore::AccessibilityObjectAtspi* findAccessibleObjectById(WebCore::AccessibilityObjectAtspi& axObject, const String& elementID)
+{
+    axObject.updateBackingStore();
+    if (axObject.id() == elementID)
+        return &axObject;
+
+    Vector<RefPtr<WebCore::AccessibilityObjectAtspi>> children;
+    InjectedBundle::singleton().accessibilityController()->executeOnAXThreadAndWait([axObject = Ref { axObject }, &children] {
+        axObject->updateBackingStore();
+        children = axObject->children();
+    });
+    for (const auto& child : children) {
+        if (auto* element = findAccessibleObjectById(*child, elementID))
+            return element;
+    }
+
+    return nullptr;
+}
+
 RefPtr<AccessibilityUIElement> AccessibilityController::accessibleElementById(JSStringRef id)
 {
+    WKBundlePageRef page = InjectedBundle::singleton().page()->page();
+    auto* rootObject = static_cast<WebCore::AccessibilityObjectAtspi*>(WKAccessibilityRootObject(page));
+    if (!rootObject)
+        return nullptr;
+
+    String elementID = toWTFString(id);
+    if (auto* element = findAccessibleObjectById(*rootObject, elementID))
+        return AccessibilityUIElement::create(element);
     return nullptr;
 }
 
 JSRetainPtr<JSStringRef> AccessibilityController::platformName()
 {
-    JSRetainPtr<JSStringRef> platformName(Adopt, JSStringCreateWithUTF8CString("atspi"));
+    // FIXME: Use atk as platform name for now, because the expected behavior is the same.
+    // Once we replace the atk implementation with the atspi one we can use atspi and
+    // update the tests helper scripts. https://bugs.webkit.org/show_bug.cgi?id=232227.
+    JSRetainPtr<JSStringRef> platformName(Adopt, JSStringCreateWithUTF8CString("atk"));
     return platformName;
 }
 
@@ -60,16 +92,15 @@
 {
     WKBundlePageRef page = InjectedBundle::singleton().page()->page();
     auto* element = static_cast<WebCore::AccessibilityObjectAtspi*>(WKAccessibilityRootObject(page));
-
     return AccessibilityUIElement::create(element);
 }
 
-Ref<AccessibilityUIElement> AccessibilityController::focusedElement()
+RefPtr<AccessibilityUIElement> AccessibilityController::focusedElement()
 {
     WKBundlePageRef page = InjectedBundle::singleton().page()->page();
-    auto* element = static_cast<WebCore::AccessibilityObjectAtspi*>(WKAccessibilityFocusedObject(page));
-
-    return AccessibilityUIElement::create(element);
+    if (auto* element = static_cast<WebCore::AccessibilityObjectAtspi*>(WKAccessibilityFocusedObject(page)))
+        return AccessibilityUIElement::create(element);
+    return nullptr;
 }
 
 bool AccessibilityController::addNotificationListener(JSValueRef functionCallback)
@@ -86,6 +117,49 @@
 {
 }
 
+RunLoop& AccessibilityController::axRunLoop()
+{
+    if (!m_axRunLoop) {
+        WKBundlePageRef page = InjectedBundle::singleton().page()->page();
+        auto* element = static_cast<WebCore::AccessibilityObjectAtspi*>(WKAccessibilityRootObject(page));
+        RELEASE_ASSERT(element);
+        m_axRunLoop = &element->root()->atspi().runLoop();
+    }
+
+    return *m_axRunLoop;
+}
+
+void AccessibilityController::executeOnAXThreadAndWait(Function<void()>&& function)
+{
+    RELEASE_ASSERT(isMainThread());
+    std::atomic<bool> done = false;
+    axRunLoop().dispatch([this, function = WTFMove(function), &done] {
+        function();
+        done.store(true);
+    });
+    while (!done.load())
+        g_main_context_iteration(nullptr, FALSE);
+}
+
+void AccessibilityController::executeOnAXThread(Function<void()>&& function)
+{
+    axRunLoop().dispatch([this, function = WTFMove(function)] {
+        function();
+    });
+}
+
+void AccessibilityController::executeOnMainThread(Function<void()>&& function)
+{
+    if (isMainThread()) {
+        function();
+        return;
+    }
+
+    axRunLoop().dispatch([this, function = WTFMove(function)]() mutable {
+        callOnMainThread(WTFMove(function));
+    });
+}
+
 } // namespace WTR
 
 #endif // HAVE(ACCESSIBILITY) && USE(ATSPI)

Modified: trunk/Tools/WebKitTestRunner/InjectedBundle/atspi/AccessibilityUIElementAtspi.cpp (285995 => 285996)


--- trunk/Tools/WebKitTestRunner/InjectedBundle/atspi/AccessibilityUIElementAtspi.cpp	2021-11-18 11:19:38 UTC (rev 285995)
+++ trunk/Tools/WebKitTestRunner/InjectedBundle/atspi/AccessibilityUIElementAtspi.cpp	2021-11-18 11:46:10 UTC (rev 285996)
@@ -31,14 +31,23 @@
 #include "InjectedBundlePage.h"
 #include <_javascript_Core/JSStringRef.h>
 #include <_javascript_Core/OpaqueJSString.h>
+#include <WebCore/AccessibilityAtspiEnums.h>
 #include <WebCore/AccessibilityObjectAtspi.h>
 #include <WebKit/WKBundleFrame.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
+#include <wtf/text/StringToIntegerConversion.h>
+#include <wtf/unicode/CharacterNames.h>
 
 namespace WTR {
 
+RefPtr<AccessibilityController> AccessibilityUIElement::s_controller;
+
 AccessibilityUIElement::AccessibilityUIElement(PlatformUIElement element)
     : m_element(element)
 {
+    if (!s_controller)
+        s_controller = InjectedBundle::singleton().accessibilityController();
 }
 
 AccessibilityUIElement::AccessibilityUIElement(const AccessibilityUIElement& other)
@@ -53,7 +62,7 @@
 
 bool AccessibilityUIElement::isEqual(AccessibilityUIElement* otherElement)
 {
-    return m_element.get() == otherElement->platformUIElement();
+    return otherElement && m_element.get() == otherElement->platformUIElement();
 }
 
 bool AccessibilityUIElement::isIsolatedObject() const
@@ -71,12 +80,18 @@
 
 int AccessibilityUIElement::childrenCount()
 {
-    return 0;
+    unsigned childCount;
+    s_controller->executeOnAXThreadAndWait([this, &childCount] {
+        m_element->updateBackingStore();
+        childCount = m_element->childCount();
+    });
+    return childCount;
 }
 
 RefPtr<AccessibilityUIElement> AccessibilityUIElement::elementAtPoint(int x, int y)
 {
-    return nullptr;
+    auto* element = m_element->hitTest({ x, y }, WebCore::Atspi::CoordinateType::WindowCoordinates);
+    return AccessibilityUIElement::create(element ? element : m_element.get());
 }
 
 unsigned AccessibilityUIElement::indexOfChild(AccessibilityUIElement* element)
@@ -86,9 +101,33 @@
 
 RefPtr<AccessibilityUIElement> AccessibilityUIElement::childAtIndex(unsigned index)
 {
-    return nullptr;
+    m_element->updateBackingStore();
+
+    RefPtr<WebCore::AccessibilityObjectAtspi> child;
+    s_controller->executeOnAXThreadAndWait([this, &child, index] {
+        m_element->updateBackingStore();
+        child = m_element->childAt(index);
+    });
+    if (!child)
+        return nullptr;
+
+    return AccessibilityUIElement::create(child.get());
 }
 
+static RefPtr<AccessibilityUIElement> elementForRelationAtIndex(WebCore::AccessibilityObjectAtspi* element, WebCore::Atspi::Relation relation, unsigned index)
+{
+    auto relationMap = element->relationMap();
+    auto targets = relationMap.get(relation);
+    if (targets.isEmpty() || index >= targets.size())
+        return nullptr;
+
+    auto target = targets[index];
+    if (!target)
+        return nullptr;
+
+    return AccessibilityUIElement::create(target.get());
+}
+
 RefPtr<AccessibilityUIElement> AccessibilityUIElement::linkedUIElementAtIndex(unsigned index)
 {
     return nullptr;
@@ -96,17 +135,17 @@
 
 RefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaOwnsElementAtIndex(unsigned index)
 {
-    return nullptr;
+    return elementForRelationAtIndex(m_element.get(), WebCore::Atspi::Relation::NodeParentOf, index);
 }
 
 RefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaFlowToElementAtIndex(unsigned index)
 {
-    return nullptr;
+    return elementForRelationAtIndex(m_element.get(), WebCore::Atspi::Relation::FlowsTo, index);
 }
 
 RefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaControlsElementAtIndex(unsigned index)
 {
-    return nullptr;
+    return elementForRelationAtIndex(m_element.get(), WebCore::Atspi::Relation::ControllerFor, index);
 }
 
 RefPtr<AccessibilityUIElement> AccessibilityUIElement::disclosedRowAtIndex(unsigned index)
@@ -136,12 +175,24 @@
 
 RefPtr<AccessibilityUIElement> AccessibilityUIElement::titleUIElement()
 {
-    return nullptr;
+    return elementForRelationAtIndex(m_element.get(), WebCore::Atspi::Relation::LabelledBy, 0);
 }
 
 RefPtr<AccessibilityUIElement> AccessibilityUIElement::parentElement()
 {
-    return nullptr;
+    m_element->updateBackingStore();
+
+    RefPtr<WebCore::AccessibilityObjectAtspi> parent;
+    s_controller->executeOnAXThreadAndWait([this, &parent] {
+        m_element->updateBackingStore();
+        auto parentWrapper = m_element->parent();
+        if (parentWrapper)
+            parent = parentWrapper.value();
+    });
+    if (!parent)
+        return nullptr;
+
+    return AccessibilityUIElement::create(parent.get());
 }
 
 RefPtr<AccessibilityUIElement> AccessibilityUIElement::disclosedByRow()
@@ -159,14 +210,104 @@
     return JSStringCreateWithCharacters(0, 0);
 }
 
+static String attributesOfElement(AccessibilityUIElement& element)
+{
+    StringBuilder builder;
+
+    builder.append(element.role()->string(), '\n');
+
+    builder.append("AXParent: ");
+    if (auto parent = element.parentElement()) {
+        builder.append(parent->role()->string().substring(8));
+        auto parentName = parent->title()->string().substring(9);
+        if (!parentName.isEmpty())
+            builder.append(": ", parentName);
+    } else
+        builder.append("(null)");
+    builder.append('\n');
+
+    builder.append("AXChildren: ", element.childrenCount(), '\n');
+
+    builder.append("AXPosition:  { ", FormattedNumber::fixedPrecision(element.x(), 6, KeepTrailingZeros));
+    builder.append(", ", FormattedNumber::fixedPrecision(element.y(), 6, KeepTrailingZeros));
+    builder.append(" }\n");
+
+    builder.append("AXSize: { ", FormattedNumber::fixedPrecision(element.width(), 6, KeepTrailingZeros));
+    builder.append(", ", FormattedNumber::fixedPrecision(element.height(), 6, KeepTrailingZeros));
+    builder.append(" }\n");
+
+    String title = element.title()->string();
+    if (!title.isEmpty()) {
+        builder.append(title);
+        builder.append('\n');
+    }
+
+    String description = element.description()->string();
+    if (!description.isEmpty())
+        builder.append(description.utf8().data(), '\n');
+
+    String value = element.stringValue()->string();
+    if (!value.isEmpty())
+        builder.append(value, '\n');
+
+    builder.append("AXFocusable: ", element.isFocusable(), '\n');
+    builder.append("AXFocused: ", element.isFocused(), '\n');
+    builder.append("AXSelectable: ", element.isSelectable(), '\n');
+    builder.append("AXSelected: ", element.isSelected(), '\n');
+    builder.append("AXMultiSelectable: ", element.isMultiSelectable(), '\n');
+    builder.append("AXEnabled: ", element.isEnabled(), '\n');
+    builder.append("AXExpanded: ", element.isExpanded(), '\n');
+    builder.append("AXRequired: ", element.isRequired(), '\n');
+    builder.append("AXChecked: ", element.isChecked(), '\n');
+
+    String url = ""
+    if (!url.isEmpty())
+        builder.append(url, '\n');
+
+    // We append the platform attributes as a single line at the end.
+    builder.append("AXPlatformAttributes: ");
+    auto attributes = element.platformUIElement()->attributes();
+    bool isFirst = true;
+    for (const auto& it : attributes) {
+        if (!isFirst)
+            builder.append(", ");
+        isFirst = false;
+        builder.append(it.key, ':', it.value);
+    }
+    builder.append('\n');
+
+    return builder.toString();
+}
+
+static String attributesOfElements(Vector<RefPtr<AccessibilityUIElement>>& elements)
+{
+    StringBuilder builder;
+    for (auto& element : elements)
+        builder.append(attributesOfElement(*element), "\n------------\n");
+    return builder.toString();
+}
+
 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfChildren()
 {
-    return JSStringCreateWithCharacters(0, 0);
+    m_element->updateBackingStore();
+
+    Vector<RefPtr<WebCore::AccessibilityObjectAtspi>> children;
+    s_controller->executeOnAXThreadAndWait([this, &children] {
+        m_element->updateBackingStore();
+        children = m_element->children();
+    });
+
+    Vector<RefPtr<AccessibilityUIElement>> elements;
+    elements.reserveInitialCapacity(children.size());
+    for (auto& child : children)
+        elements.uncheckedAppend(AccessibilityUIElement::create(child.get()));
+
+    return OpaqueJSString::tryCreate(attributesOfElements(elements)).leakRef();
 }
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::allAttributes()
 {
-    return JSStringCreateWithCharacters(0, 0);
+    return OpaqueJSString::tryCreate(attributesOfElement(*this)).leakRef();
 }
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringDescriptionOfAttributeValue(JSStringRef attribute)
@@ -176,11 +317,33 @@
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringAttributeValue(JSStringRef attribute)
 {
+    String attributeName = toWTFString(attribute);
+    if (attributeName == "AXSelectedText") {
+        if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text))
+            return JSStringCreateWithCharacters(0, 0);
+
+        m_element->updateBackingStore();
+        auto text = m_element->text();
+        auto offset = m_element->selectedRange();
+        return OpaqueJSString::tryCreate(text.substring(offset.x(), offset.y() - offset.x())).leakRef();
+    }
+
+    auto attributes = m_element->attributes();
+    if (attributeName == "AXPlaceholderValue")
+        return OpaqueJSString::tryCreate(attributes.get("placeholder-text")).leakRef();
+
     return JSStringCreateWithCharacters(0, 0);
 }
 
 double AccessibilityUIElement::numberAttributeValue(JSStringRef attribute)
 {
+    String attributeName = toWTFString(attribute);
+    auto attributes = m_element->attributes();
+    if (attributeName == "AXARIASetSize")
+        return attributes.get("setsize").toDouble();
+    if (attributeName == "AXARIAPosInSet")
+        return attributes.get("posinset").toDouble();
+
     return 0;
 }
 
@@ -204,8 +367,17 @@
     return nullptr;
 }
 
+static bool checkElementState(WebCore::AccessibilityObjectAtspi* element, WebCore::Atspi::State state)
+{
+    return element->state() & (G_GUINT64_CONSTANT(1) << state);
+}
+
 bool AccessibilityUIElement::boolAttributeValue(JSStringRef attribute)
 {
+    String attributeName = toWTFString(attribute);
+    if (attributeName == "AXElementBusy")
+        return checkElementState(m_element.get(), WebCore::Atspi::State::Busy);
+
     return false;
 }
 
@@ -216,6 +388,13 @@
 
 bool AccessibilityUIElement::isAttributeSupported(JSStringRef attribute)
 {
+    String attributeName = toWTFString(attribute);
+    auto attributes = m_element->attributes();
+    if (attributeName == "AXARIASetSize")
+        return attributes.contains("setsize");
+    if (attributeName == "AXARIAPosInSet")
+        return attributes.contains("posinset");
+
     return false;
 }
 
@@ -224,9 +403,189 @@
     return JSStringCreateWithCharacters(0, 0);
 }
 
+static String roleValueToString(unsigned roleValue)
+{
+    switch (roleValue) {
+    case WebCore::Atspi::Role::Alert:
+        return "AXAlert"_s;
+    case WebCore::Atspi::Role::Article:
+        return "AXArticle"_s;
+    case WebCore::Atspi::Role::Audio:
+        return "AXAudio"_s;
+    case WebCore::Atspi::Role::BlockQuote:
+        return "AXBlockquote"_s;
+    case WebCore::Atspi::Role::Canvas:
+        return "AXCanvas"_s;
+    case WebCore::Atspi::Role::Caption:
+        return "AXCaption"_s;
+    case WebCore::Atspi::Role::CheckBox:
+        return "AXCheckBox"_s;
+    case WebCore::Atspi::Role::CheckMenuItem:
+        return "AXCheckMenuItem"_s;
+    case WebCore::Atspi::Role::ColorChooser:
+        return "AXColorWell"_s;
+    case WebCore::Atspi::Role::ColumnHeader:
+    case WebCore::Atspi::Role::TableColumnHeader:
+        return "AXColumnHeader"_s;
+    case WebCore::Atspi::Role::ComboBox:
+        return "AXComboBox"_s;
+    case WebCore::Atspi::Role::Comment:
+        return "AXComment"_s;
+    case WebCore::Atspi::Role::ContentDeletion:
+        return "AXDeletion"_s;
+    case WebCore::Atspi::Role::ContentInsertion:
+        return "AXInsertion"_s;
+    case WebCore::Atspi::Role::Definition:
+        return "AXDefinition"_s;
+    case WebCore::Atspi::Role::DescriptionList:
+        return "AXDescriptionList"_s;
+    case WebCore::Atspi::Role::DescriptionTerm:
+        return "AXDescriptionTerm"_s;
+    case WebCore::Atspi::Role::DescriptionValue:
+        return "AXDescriptionValue"_s;
+    case WebCore::Atspi::Role::Dialog:
+        return "AXDialog"_s;
+    case WebCore::Atspi::Role::DocumentFrame:
+        return "AXDocument"_s;
+    case WebCore::Atspi::Role::DocumentWeb:
+        return "AXWebArea"_s;
+    case WebCore::Atspi::Role::Embedded:
+        return "AXEmbedded"_s;
+    case WebCore::Atspi::Role::Entry:
+        return "AXTextField"_s;
+    case WebCore::Atspi::Role::Footer:
+        return "AXFooter"_s;
+    case WebCore::Atspi::Role::Footnote:
+        return "AXFootnote"_s;
+    case WebCore::Atspi::Role::Form:
+        return "AXForm"_s;
+    case WebCore::Atspi::Role::Grouping:
+    case WebCore::Atspi::Role::Panel:
+        return "AXGroup"_s;
+    case WebCore::Atspi::Role::Heading:
+        return "AXHeading"_s;
+    case WebCore::Atspi::Role::Image:
+        return "AXImage"_s;
+    case WebCore::Atspi::Role::ImageMap:
+        return "AXImageMap"_s;
+    case WebCore::Atspi::Role::InvalidRole:
+        return "AXInvalid"_s;
+    case WebCore::Atspi::Role::Label:
+        return "AXLabel"_s;
+    case WebCore::Atspi::Role::LevelBar:
+        return "AXLevelIndicator"_s;
+    case WebCore::Atspi::Role::Link:
+        return "AXLink"_s;
+    case WebCore::Atspi::Role::ListBox:
+        return "AXListBox"_s;
+    case WebCore::Atspi::Role::List:
+        return "AXList"_s;
+    case WebCore::Atspi::Role::ListItem:
+        return "AXListItem"_s;
+    case WebCore::Atspi::Role::Log:
+        return "AXLog"_s;
+    case WebCore::Atspi::Role::Marquee:
+        return "AXMarquee"_s;
+    case WebCore::Atspi::Role::Math:
+        return "AXMath"_s;
+    case WebCore::Atspi::Role::MathFraction:
+        return "AXMathFraction"_s;
+    case WebCore::Atspi::Role::MathRoot:
+        return "AXMathRoot"_s;
+    case WebCore::Atspi::Role::Menu:
+        return "AXMenu"_s;
+    case WebCore::Atspi::Role::MenuBar:
+        return "AXMenuBar"_s;
+    case WebCore::Atspi::Role::MenuItem:
+        return "AXMenuItem"_s;
+    case WebCore::Atspi::Role::Notification:
+        return "AXNotification"_s;
+    case WebCore::Atspi::Role::PageTab:
+        return "AXTab"_s;
+    case WebCore::Atspi::Role::PageTabList:
+        return "AXTabGroup"_s;
+    case WebCore::Atspi::Role::Paragraph:
+        return "AXParagraph"_s;
+    case WebCore::Atspi::Role::PasswordText:
+        return "AXPasswordField"_s;
+    case WebCore::Atspi::Role::ProgressBar:
+        return "AXProgressIndicator"_s;
+    case WebCore::Atspi::Role::PushButton:
+        return "AXButton"_s;
+    case WebCore::Atspi::Role::RadioButton:
+        return "AXRadioButton"_s;
+    case WebCore::Atspi::Role::RadioMenuItem:
+        return "AXRadioMenuItem"_s;
+    case WebCore::Atspi::Role::RowHeader:
+    case WebCore::Atspi::Role::TableRowHeader:
+        return "AXRowHeader"_s;
+    case WebCore::Atspi::Role::Ruler:
+        return "AXRuler"_s;
+    case WebCore::Atspi::Role::ScrollBar:
+        return "AXScrollBar"_s;
+    case WebCore::Atspi::Role::ScrollPane:
+        return "AXScrollArea";
+    case WebCore::Atspi::Role::Section:
+        return "AXSection"_s;
+    case WebCore::Atspi::Role::Separator:
+        return "AXSeparator"_s;
+    case WebCore::Atspi::Role::Slider:
+        return "AXSlider"_s;
+    case WebCore::Atspi::Role::SpinButton:
+        return "AXSpinButton"_s;
+    case WebCore::Atspi::Role::Static:
+        return "AXStatic"_s;
+    case WebCore::Atspi::Role::StatusBar:
+        return "AXStatusBar"_s;
+    case WebCore::Atspi::Role::Subscript:
+        return "AXSubscript"_s;
+    case WebCore::Atspi::Role::Superscript:
+        return "AXSuperscript"_s;
+    case WebCore::Atspi::Role::Table:
+        return "AXTable"_s;
+    case WebCore::Atspi::Role::TableCell:
+        return "AXCell"_s;
+    case WebCore::Atspi::Role::TableRow:
+        return "AXRow"_s;
+    case WebCore::Atspi::Role::Timer:
+        return "AXTimer"_s;
+    case WebCore::Atspi::Role::ToggleButton:
+        return "AXToggleButton"_s;
+    case WebCore::Atspi::Role::ToolBar:
+        return "AXToolbar"_s;
+    case WebCore::Atspi::Role::ToolTip:
+        return "AXUserInterfaceTooltip"_s;
+    case WebCore::Atspi::Role::Tree:
+        return "AXTree"_s;
+    case WebCore::Atspi::Role::TreeTable:
+        return "AXTreeGrid"_s;
+    case WebCore::Atspi::Role::TreeItem:
+        return "AXTreeItem"_s;
+    case WebCore::Atspi::Role::Unknown:
+        return "AXUnknown"_s;
+    case WebCore::Atspi::Role::Video:
+        return "AXVideo"_s;
+    case WebCore::Atspi::Role::Window:
+        return "AXWindow"_s;
+    default:
+        break;
+    }
+
+    return { };
+}
+
 JSRetainPtr<JSStringRef> AccessibilityUIElement::role()
 {
-    return JSStringCreateWithCharacters(0, 0);
+    unsigned roleValue;
+    s_controller->executeOnAXThreadAndWait([this, &roleValue] {
+        m_element->updateBackingStore();
+        roleValue = m_element->role();
+    });
+    auto roleValueString = roleValueToString(roleValue);
+    if (roleValueString.isEmpty())
+        return JSStringCreateWithCharacters(0, 0);
+
+    return OpaqueJSString::tryCreate(makeString("AXRole: ", roleValueString)).leakRef();
 }
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::subrole()
@@ -236,32 +595,65 @@
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::roleDescription()
 {
-    return JSStringCreateWithCharacters(0, 0);
+    auto roleDescription = m_element->attributes().get("roledescription");
+    return OpaqueJSString::tryCreate(makeString("AXRoleDescription: ", roleDescription)).leakRef();
 }
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::computedRoleString()
 {
-    return JSStringCreateWithCharacters(0, 0);
+    auto computedRole = m_element->attributes().get("computed-role");
+    if (computedRole.isEmpty())
+        return JSStringCreateWithCharacters(0, 0);
+
+    return OpaqueJSString::tryCreate(computedRole).leakRef();
 }
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::title()
 {
-    return JSStringCreateWithCharacters(0, 0);
+    String name;
+    s_controller->executeOnAXThreadAndWait([this, &name] {
+        m_element->updateBackingStore();
+        name = String::fromUTF8(m_element->name().data());
+    });
+
+    auto titleValue = makeString("AXTitle: ", name);
+    return OpaqueJSString::tryCreate(titleValue).leakRef();
 }
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::description()
 {
-    return JSStringCreateWithCharacters(0, 0);
+    String description;
+    s_controller->executeOnAXThreadAndWait([this, &description] {
+        m_element->updateBackingStore();
+        description = String::fromUTF8(m_element->description().data());
+    });
+
+    auto descriptionValue = makeString("AXDescription: ", description);
+    return OpaqueJSString::tryCreate(descriptionValue).leakRef();
 }
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::orientation() const
 {
-    return JSStringCreateWithCharacters(0, 0);
+    const char* orientation = nullptr;
+    if (checkElementState(m_element.get(), WebCore::Atspi::State::Horizontal))
+        orientation = "AXHorizontalOrientation";
+    else if (checkElementState(m_element.get(), WebCore::Atspi::State::Vertical))
+        orientation = "AXVerticalOrientation";
+    else
+        orientation = "AXUnknownOrientation";
+
+    auto orientationValue = makeString("AXOrientation: ", orientation);
+    return OpaqueJSString::tryCreate(orientationValue).leakRef();
 }
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringValue()
 {
-    return JSStringCreateWithCharacters(0, 0);
+    if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text))
+        return JSStringCreateWithCharacters(0, 0);
+
+    m_element->updateBackingStore();
+    auto value = makeString("AXValue: ", m_element->text().replace("\n", "<\\n>").replace(objectReplacementCharacter, "<obj>"));
+    return OpaqueJSString::tryCreate(value).leakRef();
 }
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::language()
@@ -271,37 +663,56 @@
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::helpText() const
 {
-    return JSStringCreateWithCharacters(0, 0);
+    auto relationMap = m_element->relationMap();
+    auto targets = relationMap.get(WebCore::Atspi::Relation::DescribedBy);
+    if (targets.isEmpty())
+        return JSStringCreateWithCharacters(0, 0);
+
+    StringBuilder builder;
+    builder.append("AXHelp: ");
+
+    bool isFirst = true;
+    for (const auto& target : targets) {
+        if (!isFirst)
+            builder.append(' ');
+        isFirst = false;
+        target->updateBackingStore();
+        builder.append(target->text());
+    }
+
+    return OpaqueJSString::tryCreate(builder.toString()).leakRef();
 }
 
 double AccessibilityUIElement::x()
 {
-    return 0;
+    return m_element->elementRect(WebCore::Atspi::CoordinateType::ScreenCoordinates).x();
 }
 
 double AccessibilityUIElement::y()
 {
-    return 0;
+    return m_element->elementRect(WebCore::Atspi::CoordinateType::ScreenCoordinates).y();
 }
 
 double AccessibilityUIElement::width()
 {
-    return 0;
+    return m_element->elementRect(WebCore::Atspi::CoordinateType::ScreenCoordinates).width();
 }
 
 double AccessibilityUIElement::height()
 {
-    return 0;
+    return m_element->elementRect(WebCore::Atspi::CoordinateType::ScreenCoordinates).height();
 }
 
 double AccessibilityUIElement::clickPointX()
 {
-    return 0;
+    auto rect = m_element->elementRect(WebCore::Atspi::CoordinateType::WindowCoordinates);
+    return rect.center().x();
 }
 
 double AccessibilityUIElement::clickPointY()
 {
-    return 0;
+    auto rect = m_element->elementRect(WebCore::Atspi::CoordinateType::WindowCoordinates);
+    return rect.center().y();
 }
 
 double AccessibilityUIElement::intValue() const
@@ -346,47 +757,51 @@
 
 bool AccessibilityUIElement::isEnabled()
 {
-    return false;
+    return checkElementState(m_element.get(), WebCore::Atspi::State::Enabled);
 }
 
 bool AccessibilityUIElement::isRequired() const
 {
-    return false;
+    return checkElementState(m_element.get(), WebCore::Atspi::State::Required);
 }
 
 bool AccessibilityUIElement::isFocused() const
 {
-    return false;
+    return checkElementState(m_element.get(), WebCore::Atspi::State::Focused);
 }
 
 bool AccessibilityUIElement::isSelected() const
 {
-    return false;
+    return checkElementState(m_element.get(), WebCore::Atspi::State::Selected);
 }
 
 bool AccessibilityUIElement::isSelectedOptionActive() const
 {
-    return false;
+    return checkElementState(m_element.get(), WebCore::Atspi::State::Active);
 }
 
 bool AccessibilityUIElement::isExpanded() const
 {
-    return false;
+    return checkElementState(m_element.get(), WebCore::Atspi::State::Expanded);
 }
 
 bool AccessibilityUIElement::isChecked() const
 {
-    return false;
+    return checkElementState(m_element.get(), WebCore::Atspi::State::Checked);
 }
 
 bool AccessibilityUIElement::isIndeterminate() const
 {
-    return false;
+    return checkElementState(m_element.get(), WebCore::Atspi::State::Indeterminate);
 }
 
 int AccessibilityUIElement::hierarchicalLevel() const
 {
-    return false;
+    auto level = m_element->attributes().get("level");
+    if (level.isEmpty())
+        return 0;
+
+    return parseIntegerAllowingTrailingJunk<int>(level).value_or(0);
 }
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::speakAs()
@@ -396,22 +811,47 @@
 
 bool AccessibilityUIElement::ariaIsGrabbed() const
 {
-    return false;
+    return m_element->attributes().get("grabbed") == "true";
 }
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::ariaDropEffects() const
 {
-    return JSStringCreateWithCharacters(0, 0);
+    auto dropEffects = m_element->attributes().get("dropeffect");
+    if (dropEffects.isEmpty())
+        return JSStringCreateWithCharacters(0, 0);
+    return OpaqueJSString::tryCreate(dropEffects).leakRef();
 }
 
 int AccessibilityUIElement::lineForIndex(int index)
 {
-    return -1;
+    if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text))
+        return -1;
+
+    m_element->updateBackingStore();
+    auto text = m_element->text();
+    if (index < 0 || index > static_cast<int>(text.length()))
+        return -1;
+
+    int lineNumber = 0;
+    for (int i = 0; i < index; ++i) {
+        if (text[i] == '\n')
+            lineNumber++;
+    }
+
+    return lineNumber;
 }
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForLine(int line)
 {
-    return JSStringCreateWithCharacters(0, 0);
+    if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text))
+        return JSStringCreateWithCharacters(0, 0);
+
+    WebCore::IntPoint offset;
+    for (int i = 0; i <= line; ++i)
+        offset = m_element->boundaryOffset(offset.y(), WebCore::AccessibilityObjectAtspi::TextGranularity::LineStart);
+
+    auto range = makeString('{', offset.x(), ", ", offset.y() - offset.x(), '}');
+    return OpaqueJSString::tryCreate(range).leakRef();
 }
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForPosition(int x, int y)
@@ -421,17 +861,52 @@
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::boundsForRange(unsigned location, unsigned length)
 {
-    return JSStringCreateWithCharacters(0, 0);
+    if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text))
+        return JSStringCreateWithCharacters(0, 0);
+
+    auto rect = m_element->boundsForRange(location, length, WebCore::Atspi::CoordinateType::WindowCoordinates);
+    auto bounds = makeString('{', rect.x(), ", ", rect.y(), ", ", rect.width(), ", ", rect.height(), '}');
+    return OpaqueJSString::tryCreate(bounds).leakRef();
 }
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForRange(unsigned location, unsigned length)
 {
-    return JSStringCreateWithCharacters(0, 0);
+    if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text))
+        return JSStringCreateWithCharacters(0, 0);
+
+    m_element->updateBackingStore();
+    return OpaqueJSString::tryCreate(m_element->text().substring(location, length)).leakRef();
 }
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributedStringForRange(unsigned location, unsigned length)
 {
-    return JSStringCreateWithCharacters(0, 0);
+    if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text))
+        return JSStringCreateWithCharacters(0, 0);
+
+    StringBuilder builder;
+
+    auto buildAttributes = [&](const WebCore::AccessibilityObjectAtspi::TextAttributes& attributes) {
+        for (const auto& it : attributes.attributes) {
+            builder.append("\n\t\t");
+            builder.append(it.key, ':', it.value);
+        }
+    };
+
+    builder.append("\n\tDefault text attributes:");
+    buildAttributes(m_element->textAttributes());
+
+    auto text = m_element->text();
+    int endOffset = 0;
+    for (unsigned i = location; i < location + length; i = endOffset) {
+        auto attributes = m_element->textAttributes(i);
+        auto rangeStart = std::max<int>(location, attributes.startOffset);
+        auto rangeEnd = std::min<int>(location + length, attributes.endOffset);
+        builder.append("\n\tRange attributes for '", text.substring(rangeStart, rangeEnd - rangeStart).replace("\n", "<\\n>").replace(objectReplacementCharacter, "<obj>"), "':");
+        buildAttributes(attributes);
+        endOffset = attributes.endOffset;
+    }
+
+    return OpaqueJSString::tryCreate(builder.toString()).leakRef();
 }
 
 bool AccessibilityUIElement::attributedStringRangeIsMisspelled(unsigned location, unsigned length)
@@ -526,12 +1001,23 @@
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::selectedTextRange()
 {
-    return JSStringCreateWithCharacters(0, 0);
+    if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text))
+        return JSStringCreateWithCharacters(0, 0);
+
+    auto offset = m_element->selectedRange();
+    auto range = makeString('{', offset.x(), ", ", offset.y() - offset.x(), '}');
+    return OpaqueJSString::tryCreate(range).leakRef();
 }
 
 bool AccessibilityUIElement::setSelectedTextRange(unsigned location, unsigned length)
 {
-    return false;
+    if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text))
+        return false;
+
+    m_element->updateBackingStore();
+    auto textLength = m_element->text().length();
+    m_element->setSelectedRange(std::min(location, textLength), std::min(length, textLength));
+    return true;
 }
 
 void AccessibilityUIElement::increment()
@@ -598,32 +1084,32 @@
 
 bool AccessibilityUIElement::isFocusable() const
 {
-    return false;
+    return checkElementState(m_element.get(), WebCore::Atspi::State::Focusable);
 }
 
 bool AccessibilityUIElement::isSelectable() const
 {
-    return false;
+    return checkElementState(m_element.get(), WebCore::Atspi::State::Selectable);
 }
 
 bool AccessibilityUIElement::isMultiSelectable() const
 {
-    return false;
+    return checkElementState(m_element.get(), WebCore::Atspi::State::Multiselectable);
 }
 
 bool AccessibilityUIElement::isVisible() const
 {
-    return false;
+    return checkElementState(m_element.get(), WebCore::Atspi::State::Visible);
 }
 
 bool AccessibilityUIElement::isOffScreen() const
 {
-    return false;
+    return !checkElementState(m_element.get(), WebCore::Atspi::State::Showing);
 }
 
 bool AccessibilityUIElement::isCollapsed() const
 {
-    return false;
+    return checkElementState(m_element.get(), WebCore::Atspi::State::Collapsed);
 }
 
 bool AccessibilityUIElement::isIgnored() const
@@ -633,17 +1119,17 @@
 
 bool AccessibilityUIElement::isSingleLine() const
 {
-    return false;
+    return checkElementState(m_element.get(), WebCore::Atspi::State::SingleLine);
 }
 
 bool AccessibilityUIElement::isMultiLine() const
 {
-    return false;
+    return checkElementState(m_element.get(), WebCore::Atspi::State::MultiLine);
 }
 
 bool AccessibilityUIElement::hasPopup() const
 {
-    return false;
+    return checkElementState(m_element.get(), WebCore::Atspi::State::HasPopup);
 }
 
 void AccessibilityUIElement::takeFocus()
@@ -780,10 +1266,12 @@
 
 void AccessibilityUIElement::scrollToMakeVisible()
 {
+    m_element->scrollToMakeVisible(WebCore::Atspi::ScrollType::Anywhere);
 }
 
 void AccessibilityUIElement::scrollToGlobalPoint(int x, int y)
 {
+    m_element->scrollToPoint({ x, y }, WebCore::Atspi::CoordinateType::WindowCoordinates);
 }
 
 void AccessibilityUIElement::scrollToMakeVisibleWithSubFocus(int x, int y, int width, int height)
@@ -815,24 +1303,49 @@
     return nullptr;
 }
 
+static String stringAtOffset(WebCore::AccessibilityObjectAtspi* element, int offset, WebCore::AccessibilityObjectAtspi::TextGranularity granularity)
+{
+    if (!element || !element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text))
+        return { };
+
+    element->updateBackingStore();
+    auto text = element->text();
+    if (offset < 0 || offset > static_cast<int>(text.length()))
+        return { };
+
+    auto bounds = element->boundaryOffset(offset, granularity);
+    unsigned startOffset = std::max<int>(bounds.x(), 0);
+    unsigned endOffset = std::min<int>(bounds.y(), text.length());
+    return makeString(text.substring(startOffset, endOffset - startOffset), ", ", startOffset, ", ", endOffset);
+}
+
 JSRetainPtr<JSStringRef> AccessibilityUIElement::characterAtOffset(int offset)
 {
-    return JSStringCreateWithCharacters(0, 0);
+    if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text))
+        return JSStringCreateWithCharacters(0, 0);
+
+    m_element->updateBackingStore();
+    auto text = m_element->text();
+    if (offset < 0 || offset > static_cast<int>(text.length()))
+        return JSStringCreateWithCharacters(0, 0);
+
+    auto string = makeString(text.substring(offset, 1), ", ", offset, ", ", offset + 1);
+    return OpaqueJSString::tryCreate(string).leakRef();
 }
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::wordAtOffset(int offset)
 {
-    return JSStringCreateWithCharacters(0, 0);
+    return OpaqueJSString::tryCreate(stringAtOffset(m_element.get(), offset, WebCore::AccessibilityObjectAtspi::TextGranularity::WordStart)).leakRef();
 }
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::lineAtOffset(int offset)
 {
-    return JSStringCreateWithCharacters(0, 0);
+    return OpaqueJSString::tryCreate(stringAtOffset(m_element.get(), offset, WebCore::AccessibilityObjectAtspi::TextGranularity::LineStart)).leakRef();
 }
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::sentenceAtOffset(int offset)
 {
-    return JSStringCreateWithCharacters(0, 0);
+    return OpaqueJSString::tryCreate(stringAtOffset(m_element.get(), offset, WebCore::AccessibilityObjectAtspi::TextGranularity::SentenceStart)).leakRef();
 }
 
 bool AccessibilityUIElement::replaceTextInRange(JSStringRef, int, int)

Modified: trunk/Tools/WebKitTestRunner/gtk/main.cpp (285995 => 285996)


--- trunk/Tools/WebKitTestRunner/gtk/main.cpp	2021-11-18 11:19:38 UTC (rev 285995)
+++ trunk/Tools/WebKitTestRunner/gtk/main.cpp	2021-11-18 11:46:10 UTC (rev 285996)
@@ -33,6 +33,7 @@
 int main(int argc, char** argv)
 {
     g_setenv("WEBKIT_DISABLE_MEMORY_PRESSURE_MONITOR", "1", FALSE);
+    g_setenv("WEBKIT_A11Y_BUS_ADDRESS", "", FALSE);
 
     gtk_init(&argc, &argv);
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to