vcl/qa/cppunit/a11y/atspi2/atspi2.cxx   |   40 ++++++++++++++++++--------------
 vcl/qt5/QtAccessibleWidget.cxx          |    3 ++
 vcl/unx/gtk3/a11y/atktextattributes.cxx |    6 +++-
 vcl/unx/gtk4/a11y.cxx                   |    2 +
 4 files changed, 32 insertions(+), 19 deletions(-)

New commits:
commit c1cf093b1c5ccdff118c1a066e5a8e8c0470be56
Author:     Michael Weghorn <[email protected]>
AuthorDate: Wed Mar 4 19:30:05 2026 +0100
Commit:     Michael Weghorn <[email protected]>
CommitDate: Thu Mar 5 11:20:44 2026 +0100

    a11y: Handle empty extended attributes string
    
    In the toolkit a11y bridges not doing so yet (gtk3, gtk4,
    qt5/qt6), handle an empty string returned by
    XAccessibleExtendedAttributes::getExtendedAttributes
    to not report any attributes.
    
    Adjust gtk3 a11y test Atspi2TestTree::compareObjects
    accordingly as well.
    
    This prepares for an upcoming commit that will add
    a default implementation to OAccessible that returns
    an empty string.
    
    The Windows/IAccessible2 bridge already handles
    an empty string just fine.
    The macOS one currently doesn't use
    XAccessibleExtendedAttributes at all.
    
    Change-Id: I84252f8764d3385d71b9ada24125ca96762baf9d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200970
    Tested-by: Jenkins
    Reviewed-by: Michael Weghorn <[email protected]>

diff --git a/vcl/qa/cppunit/a11y/atspi2/atspi2.cxx 
b/vcl/qa/cppunit/a11y/atspi2/atspi2.cxx
index 13cb6e6b1318..cd7dff0ec59a 100644
--- a/vcl/qa/cppunit/a11y/atspi2/atspi2.cxx
+++ b/vcl/qa/cppunit/a11y/atspi2/atspi2.cxx
@@ -329,25 +329,31 @@ void Atspi2TestTree::compareObjects(const 
uno::Reference<accessibility::XAccessi
         sal_Int32 nIndex = 0;
 
         const auto atspiAttrs = pAtspiAccessible.getAttributes();
+        CPPUNIT_ASSERT_EQUAL_MESSAGE("Only one of AT-SPI attributes or 
attributes via "
+                                     "XAccessibleExtendedAttributes are empty",
+                                     sExtendedAttrs.isEmpty(), 
atspiAttrs.empty());
 
-        do
+        if (!sExtendedAttrs.isEmpty())
         {
-            OUString sProperty = sExtendedAttrs.getToken(0, ';', nIndex);
-
-            sal_Int32 nColonPos = 0;
-            const OString sPropertyName = OUStringToOString(
-                o3tl::getToken(sProperty, 0, ':', nColonPos), 
RTL_TEXTENCODING_UTF8);
-            const OString sPropertyValue = OUStringToOString(
-                o3tl::getToken(sProperty, 0, ':', nColonPos), 
RTL_TEXTENCODING_UTF8);
-
-            const auto atspiAttrIter = 
atspiAttrs.find(std::string(sPropertyName));
-            CPPUNIT_ASSERT_MESSAGE(std::string("Missing attribute: ") + 
sPropertyName.getStr(),
-                                   atspiAttrIter != atspiAttrs.end());
-            CPPUNIT_ASSERT_EQUAL(std::string_view(sPropertyName),
-                                 std::string_view(atspiAttrIter->first));
-            CPPUNIT_ASSERT_EQUAL(std::string_view(sPropertyValue),
-                                 std::string_view(atspiAttrIter->second));
-        } while (nIndex >= 0 && nIndex < sExtendedAttrs.getLength());
+            do
+            {
+                OUString sProperty = sExtendedAttrs.getToken(0, ';', nIndex);
+
+                sal_Int32 nColonPos = 0;
+                const OString sPropertyName = OUStringToOString(
+                    o3tl::getToken(sProperty, 0, ':', nColonPos), 
RTL_TEXTENCODING_UTF8);
+                const OString sPropertyValue = OUStringToOString(
+                    o3tl::getToken(sProperty, 0, ':', nColonPos), 
RTL_TEXTENCODING_UTF8);
+
+                const auto atspiAttrIter = 
atspiAttrs.find(std::string(sPropertyName));
+                CPPUNIT_ASSERT_MESSAGE(std::string("Missing attribute: ") + 
sPropertyName.getStr(),
+                                       atspiAttrIter != atspiAttrs.end());
+                CPPUNIT_ASSERT_EQUAL(std::string_view(sPropertyName),
+                                     std::string_view(atspiAttrIter->first));
+                CPPUNIT_ASSERT_EQUAL(std::string_view(sPropertyValue),
+                                     std::string_view(atspiAttrIter->second));
+            } while (nIndex >= 0 && nIndex < sExtendedAttrs.getLength());
+        }
     }
 
     // relations
diff --git a/vcl/qt5/QtAccessibleWidget.cxx b/vcl/qt5/QtAccessibleWidget.cxx
index ee74404091df..5914dcf9f0fc 100644
--- a/vcl/qt5/QtAccessibleWidget.cxx
+++ b/vcl/qt5/QtAccessibleWidget.cxx
@@ -1003,6 +1003,9 @@ QHash<QAccessible::Attribute, QVariant> 
QtAccessibleWidget::attributes() const
         return aQtAttrs;
 
     const OUString sAttrs = xAttributes->getExtendedAttributes();
+    if (sAttrs.isEmpty())
+        return aQtAttrs;
+
     sal_Int32 nIndex = 0;
     do
     {
diff --git a/vcl/unx/gtk3/a11y/atktextattributes.cxx 
b/vcl/unx/gtk3/a11y/atktextattributes.cxx
index 7510c02f21c3..c1fe05ee4478 100644
--- a/vcl/unx/gtk3/a11y/atktextattributes.cxx
+++ b/vcl/unx/gtk3/a11y/atktextattributes.cxx
@@ -1231,11 +1231,13 @@ AtkAttributeSet*
 attribute_set_new_from_extended_attributes(
     const css::uno::Reference< 
css::accessibility::XAccessibleExtendedAttributes >& rExtendedAttributes )
 {
-    AtkAttributeSet *pSet = nullptr;
-
     // extended attributes is a string of colon-separated pairs of property 
and value,
     // with pairs separated by semicolons. Example: 
"heading-level:2;weight:bold;"
     const OUString sExtendedAttrs = 
rExtendedAttributes->getExtendedAttributes();
+    if (sExtendedAttrs.isEmpty())
+        return nullptr;
+
+    AtkAttributeSet* pSet = nullptr;
     sal_Int32 nIndex = 0;
     do
     {
diff --git a/vcl/unx/gtk4/a11y.cxx b/vcl/unx/gtk4/a11y.cxx
index e68337caa6d7..f8c69b6c9e78 100644
--- a/vcl/unx/gtk4/a11y.cxx
+++ b/vcl/unx/gtk4/a11y.cxx
@@ -376,6 +376,8 @@ applyObjectAttributes(GtkAccessible* pGtkAccessible,
         return;
 
     const OUString sAttrs = xAttributes->getExtendedAttributes();
+    if (sAttrs.isEmpty())
+        return;
 
     sal_Int32 nIndex = 0;
     do

Reply via email to