WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
Patch
wtr-a11y-atspi.diff (text/plain), 56.79 KB, created by
Carlos Garcia Campos
on 2021-10-29 01:07:42 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Carlos Garcia Campos
Created:
2021-10-29 01:07:42 PDT
Size:
56.79 KB
patch
obsolete
>diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 9c5734f7ce7f..bd1f5e241197 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,20 @@ >+2021-10-25 Carlos Garcia Campos <cgarcia@igalia.com> >+ >+ [GTK][a11y] Add initial WTR accessibility support when building with ATSPI >+ https://bugs.webkit.org/show_bug.cgi?id=230259 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ 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-10-28 Carlos Garcia Campos <cgarcia@igalia.com> > > AX: AXValueChanged is only sent for range elements when value is changed with the keyboard >diff --git a/Source/WebCore/accessibility/atspi/AccessibilityAtspi.cpp b/Source/WebCore/accessibility/atspi/AccessibilityAtspi.cpp >index 332bd02e8ae2..8ffcd6276407 100644 >--- a/Source/WebCore/accessibility/atspi/AccessibilityAtspi.cpp >+++ b/Source/WebCore/accessibility/atspi/AccessibilityAtspi.cpp >@@ -48,6 +48,11 @@ AccessibilityAtspi::AccessibilityAtspi(const String& busAddress) > }); > } > >+RunLoop& AccessibilityAtspi::runLoop() const >+{ >+ return m_queue->runLoop(); >+} >+ > const char* AccessibilityAtspi::uniqueName() const > { > RELEASE_ASSERT(!isMainThread()); >diff --git a/Source/WebCore/accessibility/atspi/AccessibilityAtspi.h b/Source/WebCore/accessibility/atspi/AccessibilityAtspi.h >index 826fd445f8be..53db5bf83040 100644 >--- a/Source/WebCore/accessibility/atspi/AccessibilityAtspi.h >+++ b/Source/WebCore/accessibility/atspi/AccessibilityAtspi.h >@@ -43,6 +43,8 @@ public: > AccessibilityAtspi(const String&); > ~AccessibilityAtspi() = default; > >+ WEBCORE_EXPORT RunLoop& runLoop() const; >+ > const char* uniqueName() const; > GVariant* nullReference() const; > >diff --git a/Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.h b/Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.h >index 96977cb3b0fe..3ac6a381532a 100644 >--- a/Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.h >+++ b/Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.h >@@ -50,10 +50,10 @@ public: > 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 @@ public: > 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 @@ public: > 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(); >diff --git a/Source/WebCore/accessibility/atspi/AccessibilityRootAtspi.cpp b/Source/WebCore/accessibility/atspi/AccessibilityRootAtspi.cpp >index 6d61f626f2bc..ce8564b77192 100644 >--- a/Source/WebCore/accessibility/atspi/AccessibilityRootAtspi.cpp >+++ b/Source/WebCore/accessibility/atspi/AccessibilityRootAtspi.cpp >@@ -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,38 @@ AccessibilityObjectAtspi* AccessibilityRootAtspi::child() const > 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->focusedUIElementForPage(m_page.get()); >+ if (!focusedObject) >+ 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()); >diff --git a/Source/WebCore/accessibility/atspi/AccessibilityRootAtspi.h b/Source/WebCore/accessibility/atspi/AccessibilityRootAtspi.h >index a275f3fde46d..bd1527dde895 100644 >--- a/Source/WebCore/accessibility/atspi/AccessibilityRootAtspi.h >+++ b/Source/WebCore/accessibility/atspi/AccessibilityRootAtspi.h >@@ -49,6 +49,7 @@ public: > GVariant* applicationReference() const; > AccessibilityAtspi& atspi() const { return m_atspi; } > AccessibilityObjectAtspi* child() const; >+ AccessibilityObjectAtspi* focusedObject() const; > > void serialize(GVariantBuilder*) const; > >diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog >index 0c0d3b3e2fd8..42df399eb7f3 100644 >--- a/Source/WebKit/ChangeLog >+++ b/Source/WebKit/ChangeLog >@@ -1,3 +1,16 @@ >+2021-10-25 Carlos Garcia Campos <cgarcia@igalia.com> >+ >+ [GTK][a11y] Add initial WTR accessibility support when building with ATSPI >+ https://bugs.webkit.org/show_bug.cgi?id=230259 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ 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-10-28 Youenn Fablet <youenn@apple.com> > > Fetch API: Network process leaks when blobs are unused >diff --git a/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.cpp b/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.cpp >index 84ea591b978f..7c68ac6b1a2d 100644 >--- a/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.cpp >+++ b/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.cpp >@@ -246,7 +246,11 @@ void* WKAccessibilityRootObject(WKBundlePageRef pageRef) > #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; >@@ -273,7 +277,11 @@ void* WKAccessibilityFocusedObject(WKBundlePageRef pageRef) > #if ENABLE(ACCESSIBILITY) > 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; >diff --git a/Tools/ChangeLog b/Tools/ChangeLog >index 944dfc4e12e0..957642b882c5 100644 >--- a/Tools/ChangeLog >+++ b/Tools/ChangeLog >@@ -1,3 +1,93 @@ >+2021-10-25 Carlos Garcia Campos <cgarcia@igalia.com> >+ >+ [GTK][a11y] Add initial WTR accessibility support when building with ATSPI >+ https://bugs.webkit.org/show_bug.cgi?id=230259 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * 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-10-27 Jonathan Bedard <jbedard@apple.com> > > [webkitscmpy] Generate commit message template >diff --git a/Tools/WebKitTestRunner/InjectedBundle/AccessibilityController.h b/Tools/WebKitTestRunner/InjectedBundle/AccessibilityController.h >index e9d0f55cb0d7..90b454c6489a 100644 >--- a/Tools/WebKitTestRunner/InjectedBundle/AccessibilityController.h >+++ b/Tools/WebKitTestRunner/InjectedBundle/AccessibilityController.h >@@ -34,6 +34,7 @@ > #if PLATFORM(COCOA) > #include <wtf/RetainPtr.h> > #endif >+#include <wtf/RunLoop.h> > #include <wtf/Threading.h> > #include <wtf/threads/BinarySemaphore.h> > >@@ -69,7 +70,7 @@ public: > 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()>&&); >@@ -104,6 +105,10 @@ private: > #if ENABLE(ACCESSIBILITY_ISOLATED_TREE) > void updateIsolatedTreeMode(); > >+#if USE(ATSPI) >+ RunLoop& axRunLoop(); >+#endif >+ > #if PLATFORM(COCOA) > // _AXUIElementUseSecondaryAXThread and _AXUIElementRequestServicedBySecondaryAXThread > // do not work for WebKitTestRunner since this is calling directly into >@@ -113,6 +118,9 @@ private: > #endif > bool m_accessibilityIsolatedTreeMode { false }; > BinarySemaphore m_semaphore; >+#if USE(ATSPI) >+ RunLoop* m_axRunLoop { nullptr }; >+#endif > #endif > }; > >diff --git a/Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h b/Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h >index d354e9d4dd8b..5912a2108984 100644 >--- a/Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h >+++ b/Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h >@@ -422,6 +422,7 @@ private: > #if USE(ATK) > RefPtr<AccessibilityNotificationHandler> m_notificationHandler; > #elif USE(ATSPI) >+ static RefPtr<AccessibilityController> s_controller; > RefPtr<WebCore::AccessibilityObjectAtspi> m_element; > #endif > #endif >diff --git a/Tools/WebKitTestRunner/InjectedBundle/atspi/AccessibilityControllerAtspi.cpp b/Tools/WebKitTestRunner/InjectedBundle/atspi/AccessibilityControllerAtspi.cpp >index 40d74f323077..b7722d9e8e89 100644 >--- a/Tools/WebKitTestRunner/InjectedBundle/atspi/AccessibilityControllerAtspi.cpp >+++ b/Tools/WebKitTestRunner/InjectedBundle/atspi/AccessibilityControllerAtspi.cpp >@@ -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,46 @@ void AccessibilityController::resetToConsistentState() > { > } > >-RefPtr<AccessibilityUIElement> AccessibilityController::accessibleElementById(JSStringRef id) >+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); >+ auto* element = findAccessibleObjectById(*rootObject, elementID); >+ if (!element) >+ return nullptr; >+ >+ return AccessibilityUIElement::create(element); >+} >+ > 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; > } > >@@ -86,6 +120,46 @@ void AccessibilityController::updateIsolatedTreeMode() > { > } > >+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) >+{ >+ axRunLoop().dispatch([this, function = WTFMove(function)] { >+ function(); >+ m_semaphore.signal(); >+ }); >+ m_semaphore.wait(); >+} >+ >+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) >diff --git a/Tools/WebKitTestRunner/InjectedBundle/atspi/AccessibilityUIElementAtspi.cpp b/Tools/WebKitTestRunner/InjectedBundle/atspi/AccessibilityUIElementAtspi.cpp >index 3bcf89ddc93a..8742d65f7afb 100644 >--- a/Tools/WebKitTestRunner/InjectedBundle/atspi/AccessibilityUIElementAtspi.cpp >+++ b/Tools/WebKitTestRunner/InjectedBundle/atspi/AccessibilityUIElementAtspi.cpp >@@ -31,14 +31,23 @@ > #include "InjectedBundlePage.h" > #include <JavaScriptCore/JSStringRef.h> > #include <JavaScriptCore/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 @@ AccessibilityUIElement::~AccessibilityUIElement() > > 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,24 @@ void AccessibilityUIElement::getChildrenWithRange(Vector<RefPtr<AccessibilityUIE > > int AccessibilityUIElement::childrenCount() > { >- return 0; >+ if (!m_element) >+ 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; >+ if (!m_element) >+ 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,7 +107,37 @@ unsigned AccessibilityUIElement::indexOfChild(AccessibilityUIElement* element) > > RefPtr<AccessibilityUIElement> AccessibilityUIElement::childAtIndex(unsigned index) > { >- return nullptr; >+ if (!m_element) >+ 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) >+{ >+ if (!element) >+ return nullptr; >+ >+ 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) >@@ -96,17 +147,17 @@ RefPtr<AccessibilityUIElement> AccessibilityUIElement::linkedUIElementAtIndex(un > > 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 +187,27 @@ RefPtr<AccessibilityUIElement> AccessibilityUIElement::selectedRowAtIndex(unsign > > RefPtr<AccessibilityUIElement> AccessibilityUIElement::titleUIElement() > { >- return nullptr; >+ return elementForRelationAtIndex(m_element.get(), WebCore::Atspi::Relation::LabelledBy, 0); > } > > RefPtr<AccessibilityUIElement> AccessibilityUIElement::parentElement() > { >- return nullptr; >+ if (!m_element) >+ 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 +225,110 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfDocumentLinks() > 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 = element.url()->string(); >+ 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); >+ if (!m_element) >+ 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); >+ if (!m_element) >+ return JSStringCreateWithCharacters(0, 0); >+ >+ return OpaqueJSString::tryCreate(attributesOfElement(*this)).leakRef(); > } > > JSRetainPtr<JSStringRef> AccessibilityUIElement::stringDescriptionOfAttributeValue(JSStringRef attribute) >@@ -176,11 +338,39 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::stringDescriptionOfAttributeVal > > JSRetainPtr<JSStringRef> AccessibilityUIElement::stringAttributeValue(JSStringRef attribute) > { >+ if (!m_element) >+ return JSStringCreateWithCharacters(0, 0); >+ >+ 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) > { >+ if (!m_element) >+ return 0; >+ >+ 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 +394,20 @@ RefPtr<AccessibilityUIElement> AccessibilityUIElement::uiElementAttributeValue(J > return nullptr; > } > >+static bool checkElementState(WebCore::AccessibilityObjectAtspi* element, WebCore::Atspi::State state) >+{ >+ if (!element) >+ return false; >+ >+ 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 +418,16 @@ bool AccessibilityUIElement::isAttributeSettable(JSStringRef attribute) > > bool AccessibilityUIElement::isAttributeSupported(JSStringRef attribute) > { >+ if (!m_element) >+ return false; >+ >+ 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 +436,192 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::parameterizedAttributeNames() > 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); >+ if (!m_element) >+ 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 +631,80 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::subrole() > > JSRetainPtr<JSStringRef> AccessibilityUIElement::roleDescription() > { >- return JSStringCreateWithCharacters(0, 0); >+ if (!m_element) >+ 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); >+ if (!m_element) >+ 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); >+ if (!m_element) >+ 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); >+ if (!m_element) >+ 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); >+ if (!m_element) >+ 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 || !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 +714,77 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::language() > > JSRetainPtr<JSStringRef> AccessibilityUIElement::helpText() const > { >- return JSStringCreateWithCharacters(0, 0); >+ if (!m_element) >+ 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; >+ if (!m_element) >+ return 0; >+ >+ return m_element->elementRect(WebCore::Atspi::CoordinateType::ScreenCoordinates).x(); > } > > double AccessibilityUIElement::y() > { >- return 0; >+ if (!m_element) >+ return 0; >+ >+ return m_element->elementRect(WebCore::Atspi::CoordinateType::ScreenCoordinates).y(); > } > > double AccessibilityUIElement::width() > { >- return 0; >+ if (!m_element) >+ return 0; >+ >+ return m_element->elementRect(WebCore::Atspi::CoordinateType::ScreenCoordinates).width(); > } > > double AccessibilityUIElement::height() > { >- return 0; >+ if (!m_element) >+ return 0; >+ >+ return m_element->elementRect(WebCore::Atspi::CoordinateType::ScreenCoordinates).height(); > } > > double AccessibilityUIElement::clickPointX() > { >- return 0; >+ if (!m_element) >+ return 0; >+ >+ auto rect = m_element->elementRect(WebCore::Atspi::CoordinateType::WindowCoordinates); >+ return rect.center().x(); > } > > double AccessibilityUIElement::clickPointY() > { >- return 0; >+ if (!m_element) >+ return 0; >+ >+ auto rect = m_element->elementRect(WebCore::Atspi::CoordinateType::WindowCoordinates); >+ return rect.center().y(); > } > > double AccessibilityUIElement::intValue() const >@@ -346,47 +829,54 @@ bool AccessibilityUIElement::isDecrementActionSupported() > > 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; >+ if (!m_element) >+ return 0; >+ >+ 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 +886,53 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::speakAs() > > bool AccessibilityUIElement::ariaIsGrabbed() const > { >- return false; >+ if (!m_element) >+ return false; >+ >+ return m_element->attributes().get("grabbed") == "true"; > } > > JSRetainPtr<JSStringRef> AccessibilityUIElement::ariaDropEffects() const > { >- return JSStringCreateWithCharacters(0, 0); >+ if (!m_element) >+ 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 || !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 || !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 +942,52 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForPosition(int x, int y) > > JSRetainPtr<JSStringRef> AccessibilityUIElement::boundsForRange(unsigned location, unsigned length) > { >- return JSStringCreateWithCharacters(0, 0); >+ if (!m_element || !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 || !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 || !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 +1082,23 @@ RefPtr<AccessibilityUIElement> AccessibilityUIElement::verticalScrollbar() const > > JSRetainPtr<JSStringRef> AccessibilityUIElement::selectedTextRange() > { >- return JSStringCreateWithCharacters(0, 0); >+ if (!m_element || !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 || !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 +1165,32 @@ bool AccessibilityUIElement::removeNotificationListener() > > 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 +1200,17 @@ bool AccessibilityUIElement::isIgnored() const > > 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 +1347,18 @@ bool AccessibilityUIElement::setSelectedTextMarkerRange(AccessibilityTextMarkerR > > void AccessibilityUIElement::scrollToMakeVisible() > { >+ if (!m_element) >+ return; >+ >+ m_element->scrollToMakeVisible(WebCore::Atspi::ScrollType::Anywhere); > } > > void AccessibilityUIElement::scrollToGlobalPoint(int x, int y) > { >+ if (!m_element) >+ return; >+ >+ m_element->scrollToPoint({ x, y }, WebCore::Atspi::CoordinateType::WindowCoordinates); > } > > void AccessibilityUIElement::scrollToMakeVisibleWithSubFocus(int x, int y, int width, int height) >@@ -815,24 +1390,49 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::classList() const > 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 || !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) >diff --git a/Tools/WebKitTestRunner/gtk/main.cpp b/Tools/WebKitTestRunner/gtk/main.cpp >index b5a922a448fe..89491045be1e 100644 >--- a/Tools/WebKitTestRunner/gtk/main.cpp >+++ b/Tools/WebKitTestRunner/gtk/main.cpp >@@ -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); >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 230259
:
442353
|
442792
|
443386