include/test/a11y/accessibletestbase.hxx |   60 ++++++++++++-
 include/test/a11y/eventposter.hxx        |  132 +++++++++++++++++++++++++++++
 sw/CppunitTest_sw_a11y.mk                |    1 
 sw/qa/extras/accessibility/dialogs.cxx   |  140 +++++++++++++++++++++++++++++++
 test/Library_subsequenttest.mk           |    1 
 test/source/a11y/accessibletestbase.cxx  |   84 ++++++++++++++++++
 test/source/a11y/eventposter.cxx         |   46 ++++++++++
 7 files changed, 458 insertions(+), 6 deletions(-)

New commits:
commit be86c74bb5bf04347846261243c8eb21dc8d7200
Author:     Colomban Wendling <cwendl...@hypra.fr>
AuthorDate: Thu Nov 3 15:08:25 2022 +0100
Commit:     Michael Weghorn <m.wegh...@posteo.de>
CommitDate: Fri Mar 3 09:58:46 2023 +0000

    test: Add a few basic dialog tests and helpers
    
    This adds basic tests for a few dialogues, showcasing and exercising
    the dialog handling code.  Those tests are extremely basic but show
    that it is trivial enough to interact with a dialog.
    
    This adds a few helpers to navigate the UI using keyboard events as
    well, because it's one of the best methods to verify the actual
    interaction works for a user of assistive technologies.
    
    Change-Id: Idc1f279f35ff01769138c3addb10ef851ca0dbb8
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/142259
    Tested-by: Jenkins
    Reviewed-by: Michael Weghorn <m.wegh...@posteo.de>

diff --git a/include/test/a11y/accessibletestbase.hxx 
b/include/test/a11y/accessibletestbase.hxx
index e174c0cb4b8a..745f9fae2458 100644
--- a/include/test/a11y/accessibletestbase.hxx
+++ b/include/test/a11y/accessibletestbase.hxx
@@ -28,6 +28,7 @@
 
 #include <rtl/ustring.hxx>
 #include <test/bootstrapfixture.hxx>
+#include <test/a11y/eventposter.hxx>
 
 #include "AccessibilityTools.hxx"
 
@@ -130,13 +131,57 @@ protected:
         return activateMenuItem(menuBar, names...);
     }
 
+    /**
+     * @brief Gets the focused accessible object at @p xAcc level or below
+     * @param xAcc An accessible object
+     * @returns The accessible context of the focused object, or @c nullptr
+     *
+     * Finds the accessible object context at or under @p xAcc that has the 
focused state (and is
+     * showing).  Normally only one such object should exist in a given 
hierarchy, but in all cases
+     * this function will return the first one found.
+     *
+     * @see AccessibilityTools::getAccessibleObjectForPredicate()
+     */
+    static css::uno::Reference<css::accessibility::XAccessibleContext>
+    getFocusedObject(const 
css::uno::Reference<css::accessibility::XAccessibleContext>& xCtx);
+
+    static inline css::uno::Reference<css::accessibility::XAccessibleContext>
+    getFocusedObject(const 
css::uno::Reference<css::accessibility::XAccessible>& xAcc)
+    {
+        return getFocusedObject(xAcc->getAccessibleContext());
+    }
+
+    /**
+     * @brief Navigates through focusable elements using the Tab keyboard 
shortcut.
+     * @param xRoot The root element to look for focused elements in.
+     * @param role The accessible role of the element to tab to.
+     * @param name The accessible name of the element to tab to.
+     * @param pEventPosterHelper Pointer to a @c EventPosterHelper instance, 
or @c nullptr to obtain
+     *                           it from @p xRoot.
+     * @returns The element tabbed to, or @c nullptr if not found.
+     *
+     * Navigates through focusable elements in the top level containing @p 
xRoot using the Tab
+     * keyboard key until the focused elements matches @p role and @p name.
+     *
+     * Note that usually @p xRoot should be the toplevel accessible, or at 
least contain all
+     * focusable elements within that window.  It is however *not* a 
requirement, but only elements
+     * actually inside it will be candidate for a match, and thus if focus 
goes outside it, it might
+     * lead to not finding the target element.
+     *
+     * If @p pEventPosterHelper is @c nullptr, this function will try to 
construct one from
+     * @p xRoot.  @see EventPosterHelper.
+     */
+    static css::uno::Reference<css::accessibility::XAccessibleContext>
+    tabTo(const css::uno::Reference<css::accessibility::XAccessible>& xRoot, 
const sal_Int16 role,
+          const std::u16string_view name,
+          const EventPosterHelperBase* pEventPosterHelper = nullptr);
+
     /* Dialog handling */
-    class Dialog
+    class Dialog : public test::EventPosterHelper
     {
         friend class AccessibleTestBase;
 
     private:
-        VclPtr<vcl::Window> mxWindow;
         bool mbAutoClose;
 
         Dialog(vcl::Window* pWindow, bool bAutoClose = true);
@@ -144,9 +189,6 @@ protected:
     public:
         virtual ~Dialog();
 
-        explicit operator bool() const { return mxWindow && 
!mxWindow->isDisposed(); }
-        bool operator!() const { return !bool(*this); }
-
         void setAutoClose(bool bAutoClose) { mbAutoClose = bAutoClose; }
 
         css::uno::Reference<css::accessibility::XAccessible> getAccessible() 
const
@@ -155,6 +197,12 @@ protected:
         }
 
         bool close(sal_Int32 result = VclResponseType::RET_CANCEL);
+
+        css::uno::Reference<css::accessibility::XAccessibleContext>
+        tabTo(const sal_Int16 role, const std::u16string_view name)
+        {
+            return AccessibleTestBase::tabTo(getAccessible(), role, name, 
this);
+        }
     };
 
     class DialogWaiter
@@ -189,7 +237,9 @@ protected:
      * auto waiter = awaitDialog(u"Special Characters", [this](Dialog &dialog) 
{
      *     // for example, something like this:
      *     // something();
+     *     // CPPUNIT_ASSERT(dialog.tabTo(...));
      *     // CPPUNIT_ASSERT(somethingElse);
+     *     // dialog.postKeyEventAsync(0, awt::Key::RETURN);
      * });
      * CPPUNIT_ASSERT(activateMenuItem(u"Some menu", u"Some Item Triggering a 
Dialog..."));
      * CPPUNIT_ASSERT(waiter->waitEndDialog());
diff --git a/include/test/a11y/eventposter.hxx 
b/include/test/a11y/eventposter.hxx
new file mode 100644
index 000000000000..c0e607a70100
--- /dev/null
+++ b/include/test/a11y/eventposter.hxx
@@ -0,0 +1,132 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <test/testdllapi.hxx>
+
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/accessibility/XAccessibleContext.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <rtl/ustring.hxx>
+#include <vcl/window.hxx>
+
+namespace test
+{
+/**
+ * @brief Base helper class to send events to a window.
+ *
+ * Implementations of this helper will usually just wrap an implementation of 
post*Event*() calls.
+ * This class is mostly useful to encapsulate the calls when getting the 
target window is not
+ * trivial or is only relevant to sending events, and to have a generic event 
poster interface.
+ *
+ * Additionally, this class provides simplified helpers to send event pairs, 
like key down/up, or
+ * text+commit, to make it easier on the common case for callers.
+ */
+class OOO_DLLPUBLIC_TEST EventPosterHelperBase
+{
+public:
+    virtual ~EventPosterHelperBase(){};
+
+    /** @see SfxLokHelper::postKeyEventAsync */
+    virtual void postKeyEventAsync(int nType, int nCharCode, int nKeyCode) 
const = 0;
+
+    /** Posts a full key down/up cycle */
+    void postKeyEventAsync(int nCharCode, int nKeyCode) const
+    {
+        postKeyEventAsync(LOK_KEYEVENT_KEYINPUT, nCharCode, nKeyCode);
+        postKeyEventAsync(LOK_KEYEVENT_KEYUP, nCharCode, nKeyCode);
+    }
+
+    /** @see SfxLokHelper::postExtTextEventAsync */
+    virtual void postExtTextEventAsync(int nType, const OUString& rText) const 
= 0;
+
+    /** Posts a full text input + commit sequence */
+    void postExtTextEventAsync(const OUString& rText) const
+    {
+        postExtTextEventAsync(LOK_EXT_TEXTINPUT, rText);
+        postExtTextEventAsync(LOK_EXT_TEXTINPUT_END, rText);
+    }
+};
+
+/**
+ * @brief Helper to send events to a window.
+ *
+ * This helper basically just wraps SfxLokHelper::post*EventAsync() calls to 
hold the target window
+ * reference in the class.
+ */
+class OOO_DLLPUBLIC_TEST EventPosterHelper : public EventPosterHelperBase
+{
+protected:
+    VclPtr<vcl::Window> mxWindow;
+
+public:
+    EventPosterHelper(void)
+        : mxWindow(nullptr)
+    {
+    }
+    EventPosterHelper(VclPtr<vcl::Window> xWindow)
+        : mxWindow(xWindow)
+    {
+    }
+    EventPosterHelper(vcl::Window* pWindow)
+        : mxWindow(pWindow)
+    {
+    }
+
+    vcl::Window* getWindow() const { return mxWindow; }
+
+    void setWindow(VclPtr<vcl::Window> xWindow) { mxWindow = xWindow; }
+    void setWindow(vcl::Window* pWindow) { mxWindow = pWindow; }
+
+    explicit operator bool() const { return mxWindow && 
!mxWindow->isDisposed(); }
+    bool operator!() const { return !bool(*this); }
+
+    using EventPosterHelperBase::postKeyEventAsync;
+    using EventPosterHelperBase::postExtTextEventAsync;
+
+    /** @see SfxLokHelper::postKeyEventAsync */
+    virtual void postKeyEventAsync(int nType, int nCharCode, int nKeyCode) 
const override;
+    /** @see SfxLokHelper::postExtTextEventAsync */
+    virtual void postExtTextEventAsync(int nType, const OUString& rText) const 
override;
+};
+
+/**
+ * @brief Accessibility-specialized helper to send events to a window.
+ *
+ * This augments @c test::EventPosterHelper to simplify usage in accessibility 
tests.
+ */
+class OOO_DLLPUBLIC_TEST AccessibleEventPosterHelper : public EventPosterHelper
+{
+public:
+    AccessibleEventPosterHelper(void)
+        : EventPosterHelper()
+    {
+    }
+    AccessibleEventPosterHelper(const 
css::uno::Reference<css::accessibility::XAccessible> xAcc)
+    {
+        setWindow(xAcc);
+    }
+
+    /**
+     * @brief Sets the window on which post events based on an accessible 
object inside it.
+     * @param xAcc An accessible object inside a toplevel.
+     *
+     * This method tries and find the top level window containing @p xAcc to 
use it to post events.
+     *
+     * This currently relies on a toplevel accessible being a @c VCLXWindow, 
and requires that
+     * window's output device to be set (@see VCLXWindow::GetWindow()).
+     */
+    void setWindow(css::uno::Reference<css::accessibility::XAccessible> xAcc);
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/sw/CppunitTest_sw_a11y.mk b/sw/CppunitTest_sw_a11y.mk
index 67c527d1179c..282f580423aa 100644
--- a/sw/CppunitTest_sw_a11y.mk
+++ b/sw/CppunitTest_sw_a11y.mk
@@ -11,6 +11,7 @@ $(eval $(call gb_CppunitTest_CppunitTest,sw_a11y))
 
 $(eval $(call gb_CppunitTest_add_exception_objects,sw_a11y, \
        sw/qa/extras/accessibility/basics \
+       sw/qa/extras/accessibility/dialogs \
 ))
 
 $(eval $(call gb_CppunitTest_use_libraries,sw_a11y, \
diff --git a/sw/qa/extras/accessibility/dialogs.cxx 
b/sw/qa/extras/accessibility/dialogs.cxx
new file mode 100644
index 000000000000..ab03af5e22db
--- /dev/null
+++ b/sw/qa/extras/accessibility/dialogs.cxx
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <com/sun/star/awt/Key.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+
+#include <vcl/scheduler.hxx>
+
+#include <test/a11y/swaccessibletestbase.hxx>
+#include <test/a11y/AccessibilityTools.hxx>
+
+using namespace css;
+
+// FIXME: dialog doesn't pop up on macos and doesn't close on win32...
+#if !defined(_WIN32) && !defined(MACOSX)
+CPPUNIT_TEST_FIXTURE(test::SwAccessibleTestBase, BasicTestHyperlinkDialog)
+{
+    load(u"private:factory/swriter");
+
+    auto dialogWaiter = awaitDialog(u"Hyperlink", [this](Dialog& dialog) {
+        dumpA11YTree(dialog.getAccessible()->getAccessibleContext());
+
+        // Focus the URL box (should be default, but make sure we're on it)
+        CPPUNIT_ASSERT(dialog.tabTo(accessibility::AccessibleRole::COMBO_BOX, 
u"URL:"));
+        // Fill in an address
+        dialog.postExtTextEventAsync(u"https://libreoffice.org/";);
+        // Validate the whole dialog
+        dialog.postKeyEventAsync(0, awt::Key::RETURN);
+        Scheduler::ProcessEventsToIdle();
+    });
+
+    // Activate the Insert->Hyperlink... menu item to open the Hyperlink dialog
+    CPPUNIT_ASSERT(activateMenuItem(u"Insert", u"Hyperlink..."));
+    CPPUNIT_ASSERT(dialogWaiter->waitEndDialog());
+
+    
CPPUNIT_ASSERT_EQUAL(rtl::OUString("<PARAGRAPH>https://libreoffice.org/</PARAGRAPH>"),
+                         collectText());
+}
+#endif
+
+// FIXME: dialog doesn't pop up on macos and doesn't close on win32...
+#if !defined(_WIN32) && !defined(MACOSX)
+CPPUNIT_TEST_FIXTURE(test::SwAccessibleTestBase, BasicTestBookmarkDialog)
+{
+    load(u"private:factory/swriter");
+
+    auto dialogWaiter = awaitDialog(u"Bookmark", [this](Dialog& dialog) {
+        dumpA11YTree(dialog.getAccessible()->getAccessibleContext());
+
+        CPPUNIT_ASSERT(dialog.tabTo(accessibility::AccessibleRole::TEXT, 
u"Name:"));
+        dialog.postKeyEventAsync(0, awt::Key::SELECT_ALL);
+        dialog.postKeyEventAsync(0, awt::Key::DELETE);
+        dialog.postExtTextEventAsync(u"Test Bookmark 1");
+        // Validate the whole dialog
+        dialog.postKeyEventAsync(0, awt::Key::RETURN);
+        Scheduler::ProcessEventsToIdle();
+    });
+
+    // Activate the Insert->Bookmark... menu item to open the Bookmark dialog
+    CPPUNIT_ASSERT(activateMenuItem(u"Insert", u"Bookmark..."));
+    CPPUNIT_ASSERT(dialogWaiter->waitEndDialog());
+
+    CPPUNIT_ASSERT_EQUAL(rtl::OUString("<PARAGRAPH>#Test Bookmark 1 Bookmark 
</PARAGRAPH>"),
+                         collectText());
+}
+#endif
+
+// FIXME: dialog doesn't pop up on macos and doesn't close on win32...
+#if !defined(_WIN32) && !defined(MACOSX)
+CPPUNIT_TEST_FIXTURE(test::SwAccessibleTestBase, BasicTestSectionDialog)
+{
+    load(u"private:factory/swriter");
+
+    auto dialogWaiter = awaitDialog(u"Insert Section", [this](Dialog& dialog) {
+        dumpA11YTree(dialog.getAccessible()->getAccessibleContext());
+
+        // Validate the whole dialog
+        dialog.postKeyEventAsync(0, awt::Key::RETURN);
+        Scheduler::ProcessEventsToIdle();
+    });
+
+    CPPUNIT_ASSERT(activateMenuItem(u"Insert", u"Section..."));
+    CPPUNIT_ASSERT(dialogWaiter->waitEndDialog());
+
+    CPPUNIT_ASSERT_EQUAL(rtl::OUString("<PARAGRAPH/><PARAGRAPH/>"), 
collectText());
+}
+#endif
+
+// FIXME: dialog doesn't pop up on macos and doesn't close on win32...
+#if !defined(_WIN32) && !defined(MACOSX)
+CPPUNIT_TEST_FIXTURE(test::SwAccessibleTestBase, BasicTestFontworkDialog)
+{
+    load(u"private:factory/swriter");
+
+    auto dialogWaiter = awaitDialog(u"Fontwork Gallery", [this](Dialog& 
dialog) {
+        dumpA11YTree(dialog.getAccessible()->getAccessibleContext());
+
+        // Validate the whole dialog
+        dialog.postKeyEventAsync(0, awt::Key::RETURN);
+        Scheduler::ProcessEventsToIdle();
+    });
+
+    CPPUNIT_ASSERT(activateMenuItem(u"Insert", u"Fontwork..."));
+    CPPUNIT_ASSERT(dialogWaiter->waitEndDialog());
+
+    CPPUNIT_ASSERT_EQUAL(
+        rtl::OUString("<PARAGRAPH/><SHAPE name=\"Simple\" description=\" 
\"><PARAGRAPH "
+                      "description=\"Paragraph: 0 
Simple\">Simple</PARAGRAPH></SHAPE>"),
+        collectText());
+}
+#endif
+
+// FIXME: dialog doesn't pop up on macos and doesn't close on win32...
+#if !defined(_WIN32) && !defined(MACOSX)
+CPPUNIT_TEST_FIXTURE(test::SwAccessibleTestBase, BasicTestFrameDialog)
+{
+    load(u"private:factory/swriter");
+
+    auto dialogWaiter = awaitDialog(u"Frame", [](Dialog& dialog) {
+        // Validate the whole dialog
+        dialog.postKeyEventAsync(0, awt::Key::RETURN);
+        Scheduler::ProcessEventsToIdle();
+    });
+
+    CPPUNIT_ASSERT(activateMenuItem(u"Insert", u"Frame", u"Frame..."));
+    CPPUNIT_ASSERT(dialogWaiter->waitEndDialog());
+
+    CPPUNIT_ASSERT_EQUAL(
+        rtl::OUString("<PARAGRAPH/><TEXT_FRAME 
name=\"Frame1\"><PARAGRAPH/></TEXT_FRAME>"),
+        collectText());
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/test/Library_subsequenttest.mk b/test/Library_subsequenttest.mk
index 1e94db958852..7323f7312d93 100644
--- a/test/Library_subsequenttest.mk
+++ b/test/Library_subsequenttest.mk
@@ -47,6 +47,7 @@ $(eval $(call 
gb_Library_add_exception_objects,subsequenttest,\
        test/source/unoapixml_test \
        test/source/a11y/AccessibilityTools \
        test/source/a11y/accessibletestbase \
+       test/source/a11y/eventposter \
        test/source/a11y/swaccessibletestbase \
        test/source/beans/xpropertyset \
        test/source/chart/xchartdata \
diff --git a/test/source/a11y/accessibletestbase.cxx 
b/test/source/a11y/accessibletestbase.cxx
index 857df519f136..d9cfaa731f95 100644
--- a/test/source/a11y/accessibletestbase.cxx
+++ b/test/source/a11y/accessibletestbase.cxx
@@ -235,10 +235,92 @@ bool test::AccessibleTestBase::activateMenuItem(
     return false;
 }
 
+uno::Reference<accessibility::XAccessibleContext> 
test::AccessibleTestBase::getFocusedObject(
+    const uno::Reference<accessibility::XAccessibleContext>& xCtx)
+{
+    return AccessibilityTools::getAccessibleObjectForPredicate(
+        xCtx, [](const uno::Reference<accessibility::XAccessibleContext>& 
xCandidateCtx) {
+            const auto states = (accessibility::AccessibleStateType::FOCUSED
+                                 | 
accessibility::AccessibleStateType::SHOWING);
+            return (xCandidateCtx->getAccessibleStateSet() & states) == states;
+        });
+}
+
+uno::Reference<accessibility::XAccessibleContext>
+test::AccessibleTestBase::tabTo(const 
uno::Reference<accessibility::XAccessible>& xRoot,
+                                const sal_Int16 role, const 
std::u16string_view name,
+                                const EventPosterHelperBase* 
pEventPosterHelper)
+{
+    AccessibleEventPosterHelper eventHelper;
+    if (!pEventPosterHelper)
+    {
+        eventHelper.setWindow(xRoot);
+        pEventPosterHelper = &eventHelper;
+    }
+
+    auto xOriginalFocus = getFocusedObject(xRoot);
+    auto xFocus = xOriginalFocus;
+    int nSteps = 0;
+
+    std::cout << "Tabbing to '" << OUString(name) << "'..." << std::endl;
+    while (xFocus && (nSteps == 0 || xFocus != xOriginalFocus))
+    {
+        std::cout << "  focused object is: " << 
AccessibilityTools::debugString(xFocus)
+                  << std::endl;
+        if (xFocus->getAccessibleRole() == role && 
AccessibilityTools::nameEquals(xFocus, name))
+        {
+            std::cout << "  -> OK, focus matches" << std::endl;
+            return xFocus;
+        }
+        if (++nSteps > 100)
+        {
+            std::cerr << "Object not found after tabbing 100 times! bailing 
out" << std::endl;
+            break;
+        }
+
+        std::cout << "  -> no match, sending <TAB>" << std::endl;
+        pEventPosterHelper->postKeyEventAsync(0, awt::Key::TAB);
+        Scheduler::ProcessEventsToIdle();
+
+        const auto xPrevFocus = xFocus;
+        xFocus = getFocusedObject(xRoot);
+        if (!xFocus)
+            std::cerr << "Focus lost after sending <TAB>!" << std::endl;
+        else if (xPrevFocus == xFocus)
+        {
+            std::cerr << "Focus didn't move after sending <TAB>! bailing out" 
<< std::endl;
+            std::cerr << "Focused object(s):" << std::endl;
+            int iFocusedCount = 0;
+            // count and print out objects with focused state
+            AccessibilityTools::getAccessibleObjectForPredicate(
+                xRoot,
+                [&iFocusedCount](const 
uno::Reference<accessibility::XAccessibleContext>& xCtx) {
+                    const auto states = 
(accessibility::AccessibleStateType::FOCUSED
+                                         | 
accessibility::AccessibleStateType::SHOWING);
+                    if ((xCtx->getAccessibleStateSet() & states) == states)
+                    {
+                        std::cerr << " * " << 
AccessibilityTools::debugString(xCtx) << std::endl;
+                        iFocusedCount++;
+                    }
+                    return false; // keep going
+                });
+            std::cerr << "Total focused element(s): " << iFocusedCount << 
std::endl;
+            if (iFocusedCount > 1)
+                std::cerr << "WARNING: there are more than one focused object! 
This usually means "
+                             "there is a BUG in the focus handling of that 
accessibility tree."
+                          << std::endl;
+            break;
+        }
+    }
+
+    std::cerr << "NOT FOUND" << std::endl;
+    return nullptr;
+}
+
 /* Dialog handling */
 
 test::AccessibleTestBase::Dialog::Dialog(vcl::Window* pWindow, bool bAutoClose)
-    : mxWindow(pWindow)
+    : test::EventPosterHelper(pWindow)
     , mbAutoClose(bAutoClose)
 {
     CPPUNIT_ASSERT(pWindow);
diff --git a/test/source/a11y/eventposter.cxx b/test/source/a11y/eventposter.cxx
new file mode 100644
index 000000000000..39e178e22756
--- /dev/null
+++ b/test/source/a11y/eventposter.cxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <test/a11y/eventposter.hxx>
+
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/accessibility/XAccessibleContext.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+
+#include <sfx2/lokhelper.hxx>
+#include <test/a11y/AccessibilityTools.hxx>
+#include <toolkit/awt/vclxwindow.hxx>
+
+void test::EventPosterHelper::postKeyEventAsync(int nType, int nCharCode, int 
nKeyCode) const
+{
+    SfxLokHelper::postKeyEventAsync(mxWindow, nType, nCharCode, nKeyCode);
+}
+
+void test::EventPosterHelper::postExtTextEventAsync(int nType, const OUString& 
rText) const
+{
+    SfxLokHelper::postExtTextEventAsync(mxWindow, nType, rText);
+}
+
+void test::AccessibleEventPosterHelper::setWindow(
+    css::uno::Reference<css::accessibility::XAccessible> xAcc)
+{
+    while (auto xParent = xAcc->getAccessibleContext()->getAccessibleParent())
+        xAcc = xParent;
+    auto vclXWindow = dynamic_cast<VCLXWindow*>(xAcc.get());
+    if (!vclXWindow)
+    {
+        std::cerr << "WARNING: AccessibleEventPosterHelper::setWindow() called 
on "
+                     "unsupported object "
+                  << AccessibilityTools::debugString(xAcc) << ". Event 
delivery will not work."
+                  << std::endl;
+    }
+    mxWindow = vclXWindow ? vclXWindow->GetWindow() : nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */

Reply via email to