14#include <com/sun/star/accessibility/AccessibleRole.hpp>
15#include <com/sun/star/accessibility/AccessibleStateType.hpp>
16#include <com/sun/star/accessibility/XAccessible.hpp>
17#include <com/sun/star/accessibility/XAccessibleAction.hpp>
18#include <com/sun/star/accessibility/XAccessibleContext.hpp>
19#include <com/sun/star/awt/XDialog2.hpp>
20#include <com/sun/star/awt/XExtendedToolkit.hpp>
21#include <com/sun/star/awt/XTopWindow.hpp>
22#include <com/sun/star/awt/XTopWindowListener.hpp>
23#include <com/sun/star/frame/Desktop.hpp>
24#include <com/sun/star/frame/FrameSearchFlag.hpp>
25#include <com/sun/star/frame/XFrame.hpp>
26#include <com/sun/star/frame/XFrame2.hpp>
27#include <com/sun/star/frame/XModel.hpp>
28#include <com/sun/star/uno/Reference.hxx>
29#include <com/sun/star/uno/RuntimeException.hpp>
30#include <com/sun/star/util/XCloseable.hpp>
53 uno::Reference<util::XCloseable> xCloseable(mxDocument, uno::UNO_QUERY_THROW);
54 xCloseable->close(
false);
65 mxDocument = mxDesktop->loadComponentFromURL(sURL,
"_blank", frame::FrameSearchFlag::AUTO, {});
67 uno::Reference<frame::XModel>
xModel(mxDocument, uno::UNO_QUERY_THROW);
68 mxWindow.set(
xModel->getCurrentController()->getFrame()->getContainerWindow());
71 uno::Reference<awt::XTopWindow> xTopWindow(mxWindow, uno::UNO_QUERY_THROW);
72 xTopWindow->toFront();
77 load(m_directories.getURLFromSrc(sSrcPath));
80uno::Reference<accessibility::XAccessibleContext>
83 uno::Reference<accessibility::XAccessible> xAccessible(mxWindow, uno::UNO_QUERY_THROW);
85 return xAccessible->getAccessibleContext();
90 return (role == accessibility::AccessibleRole::DOCUMENT
91 || role == accessibility::AccessibleRole::DOCUMENT_PRESENTATION
92 || role == accessibility::AccessibleRole::DOCUMENT_SPREADSHEET
93 || role == accessibility::AccessibleRole::DOCUMENT_TEXT);
96uno::Reference<accessibility::XAccessibleContext>
99 uno::Reference<frame::XModel>
xModel(mxDocument, uno::UNO_QUERY_THROW);
100 uno::Reference<accessibility::XAccessible> xAccessible(
101 xModel->getCurrentController()->getFrame()->getComponentWindow(), uno::UNO_QUERY_THROW);
104 xAccessible->getAccessibleContext(),
105 [](
const uno::Reference<accessibility::XAccessibleContext>& xCtx) {
106 return (isDocumentRole(xCtx->getAccessibleRole())
107 && xCtx->getAccessibleStateSet() & accessibility::AccessibleStateType::SHOWING);
111uno::Reference<accessibility::XAccessibleContext>
113 const uno::Reference<accessibility::XAccessibleContext>& xContext, sal_Int16 relationType)
115 auto relset = xContext->getAccessibleRelationSet();
119 for (sal_Int32
i = 0;
i < relset->getRelationCount(); ++
i)
121 const auto& rel = relset->getRelation(
i);
122 if (rel.RelationType == relationType)
124 for (
auto& target : rel.TargetSet)
126 uno::Reference<accessibility::XAccessible> targetAccessible(target,
128 if (targetAccessible.is())
129 return targetAccessible->getAccessibleContext();
138std::deque<uno::Reference<accessibility::XAccessibleContext>>
140 const uno::Reference<accessibility::XAccessibleContext>& xContext)
142 std::deque<uno::Reference<accessibility::XAccessibleContext>> children;
143 auto childCount = xContext->getAccessibleChildCount();
147 auto child = xContext->getAccessibleChild(
i);
148 children.push_back(child->getAccessibleContext());
156 const uno::Reference<accessibility::XAccessibleContext>& xContext,
const int depth)
159 auto xRelSet = xContext->getAccessibleRelationSet();
166 auto relCount = xRelSet->getRelationCount();
169 std::cout <<
" rels=[";
170 for (sal_Int32
i = 0;
i < relCount; ++
i)
175 const auto& rel = xRelSet->getRelation(
i);
177 <<
" (" << rel.RelationType <<
")";
178 std::cout <<
" targets=[";
180 for (
auto& target : rel.TargetSet)
184 uno::Reference<accessibility::XAccessible> ta(target, uno::UNO_QUERY_THROW);
192 std::cout << std::endl;
195 for (
auto& child : getAllChildren(xContext))
197 for (
int j = 0; j < depth; j++)
199 std::cout <<
" * child " <<
i++ <<
": ";
200 dumpA11YTree(child, depth + 1);
206 const uno::Reference<accessibility::XAccessibleContext>& xMenuCtx, std::u16string_view name)
208 auto childCount = xMenuCtx->getAccessibleChildCount();
210 std::cout <<
"looking up item " << OUString(name) <<
" in "
214 auto item = xMenuCtx->getAccessibleChild(i)->getAccessibleContext();
222 std::cout <<
"-> NOT FOUND!" << std::endl;
223 std::cout <<
" Contents was: ";
224 dumpA11YTree(xMenuCtx, 1);
226 return uno::Reference<accessibility::XAccessibleContext>();
230 const uno::Reference<accessibility::XAccessibleAction>& xAction)
233 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xAction->getAccessibleActionCount());
234 if (xAction->doAccessibleAction(0))
243 const uno::Reference<accessibility::XAccessibleContext>& xCtx)
246 xCtx, [](
const uno::Reference<accessibility::XAccessibleContext>& xCandidateCtx) {
247 const auto states = (accessibility::AccessibleStateType::FOCUSED
248 | accessibility::AccessibleStateType::SHOWING);
249 return (xCandidateCtx->getAccessibleStateSet() & states) == states;
253uno::Reference<accessibility::XAccessibleContext>
255 const sal_Int16 role,
const std::u16string_view name,
256 const EventPosterHelperBase* pEventPosterHelper)
258 AccessibleEventPosterHelper eventHelper;
259 if (!pEventPosterHelper)
261 eventHelper.setWindow(xRoot);
262 pEventPosterHelper = &eventHelper;
265 auto xOriginalFocus = getFocusedObject(xRoot);
266 auto xFocus = xOriginalFocus;
269 std::cout <<
"Tabbing to '" << OUString(name) <<
"'..." << std::endl;
270 while (xFocus && (nSteps == 0 || xFocus != xOriginalFocus))
276 std::cout <<
" -> OK, focus matches" << std::endl;
281 std::cerr <<
"Object not found after tabbing 100 times! bailing out" << std::endl;
285 std::cout <<
" -> no match, sending <TAB>" << std::endl;
286 pEventPosterHelper->postKeyEventAsync(0, awt::Key::TAB);
289 const auto xPrevFocus = xFocus;
290 xFocus = getFocusedObject(xRoot);
292 std::cerr <<
"Focus lost after sending <TAB>!" << std::endl;
293 else if (xPrevFocus == xFocus)
295 std::cerr <<
"Focus didn't move after sending <TAB>! bailing out" << std::endl;
296 std::cerr <<
"Focused object(s):" << std::endl;
297 int iFocusedCount = 0;
301 [&iFocusedCount](
const uno::Reference<accessibility::XAccessibleContext>& xCtx) {
302 const auto states = (accessibility::AccessibleStateType::FOCUSED
303 | accessibility::AccessibleStateType::SHOWING);
304 if ((xCtx->getAccessibleStateSet() & states) == states)
311 std::cerr <<
"Total focused element(s): " << iFocusedCount << std::endl;
312 if (iFocusedCount > 1)
313 std::cerr <<
"WARNING: there are more than one focused object! This usually means "
314 "there is a BUG in the focus handling of that accessibility tree."
320 std::cerr <<
"NOT FOUND" << std::endl;
325 const uno::Reference<accessibility::XAccessible>& xRoot,
326 const uno::Reference<accessibility::XAccessibleContext>& xChild,
327 const EventPosterHelperBase* pEventPosterHelper)
329 AccessibleEventPosterHelper eventHelper;
330 if (!pEventPosterHelper)
332 eventHelper.setWindow(xRoot);
333 pEventPosterHelper = &eventHelper;
337 for (
int i = 0;
i < 100;
i++)
339 if (xChild->getAccessibleStateSet() & accessibility::AccessibleStateType::FOCUSED)
342 std::cout <<
" no match, sending <TAB>" << std::endl;
343 pEventPosterHelper->postKeyEventAsync(0, awt::Key::TAB);
347 std::cerr <<
"NOT FOUND" << std::endl;
360 : mbAutoClose(bAutoClose)
361 , mxDialog2(xDialog2)
363 CPPUNIT_ASSERT(xDialog2.is());
370 std::cerr <<
"WARNING: AccessibleTestBase::Dialog() constructed with awt::XDialog2 '"
371 << xDialog2->getTitle()
372 <<
"' not implementing accessibility::XAccessible. Event delivery will not work."
387 mxDialog2->endDialog(
result);
392std::shared_ptr<test::AccessibleTestBase::DialogWaiter>
394 std::function<
void(
Dialog&)> callback,
bool bAutoClose)
406 uno::Reference<awt::XExtendedToolkit> mxToolkit;
407 bool mbWaitingForDialog;
408 std::exception_ptr mpException;
409 std::u16string_view
msName;
410 std::function<void(
Dialog&)> mCallback;
412 Timer maTimeoutTimer;
414 uno::Reference<awt::XTopWindowListener> mxTopWindowListener;
415 std::unique_ptr<Dialog> mxDialog;
418 virtual ~ListenerHelper()
421 mxToolkit->removeTopWindowListener(mxTopWindowListener);
422 maTimeoutTimer.
Stop();
423 maIdleHandler.
Stop();
426 ListenerHelper(
const std::u16string_view&
name, std::function<
void(
Dialog&)> callback,
428 : mbWaitingForDialog(
true)
430 , mCallback(callback)
431 , mbAutoClose(bAutoClose)
432 , maTimeoutTimer(
"workaround timer if we don't catch WindowActivate")
433 , maIdleHandler(
"runs user callback in idle time")
435 mxTopWindowListener.set(
new MyTopWindowListener(
this));
437 mxToolkit->addTopWindowListener(mxTopWindowListener);
441 maTimeoutTimer.
Start();
444 maIdleHandler.
SetPriority(TaskPriority::DEFAULT_IDLE);
452 static void LinkStubtimeoutTimerHandler(
void* instance,
Timer* timer)
454 static_cast<ListenerHelper*
>(instance)->timeoutTimerHandler(timer);
457 void timeoutTimerHandler(
Timer*)
459 std::cerr <<
"timeout waiting for dialog '" << OUString(
msName) <<
"' to show up"
462 assert(mbWaitingForDialog);
466 throw new css::uno::RuntimeException(
"Timeout waiting for dialog");
469 class MyTopWindowListener :
public ::cppu::WeakImplHelper<awt::XTopWindowListener>
472 ListenerHelper* mpHelper;
475 MyTopWindowListener(ListenerHelper* pHelper)
482 virtual void SAL_CALL
windowOpened(
const lang::EventObject&)
override {}
483 virtual void SAL_CALL
windowClosing(
const lang::EventObject&)
override {}
484 virtual void SAL_CALL
windowClosed(
const lang::EventObject&)
override {}
485 virtual void SAL_CALL
windowMinimized(
const lang::EventObject&)
override {}
486 virtual void SAL_CALL
windowNormalized(
const lang::EventObject&)
override {}
488 virtual void SAL_CALL
windowActivated(
const lang::EventObject& xEvent)
override
490 assert(mpHelper->mbWaitingForDialog);
495 uno::Reference<awt::XDialog2> xDialog(xEvent.Source, uno::UNO_QUERY);
500 mpHelper->mxToolkit->removeTopWindowListener(
this);
502 mpHelper->mxDialog = std::make_unique<Dialog>(xDialog,
true);
504 mpHelper->maIdleHandler.Start();
508 virtual void SAL_CALL disposing(
const lang::EventObject&)
override {}
512 static void LinkStubidleHandler(
void* instance,
Timer* idle)
514 static_cast<ListenerHelper*
>(instance)->idleHandler(idle);
517 void idleHandler(
Timer*)
519 mbWaitingForDialog =
false;
522 maTimeoutTimer.
Stop();
527 if (
msName != mxDialog->getWindow()->GetText())
529 mpException = std::make_exception_ptr(css::uno::RuntimeException(
530 "Unexpected dialog '" + mxDialog->getWindow()->GetText()
531 +
"' opened instead of the expected '" +
msName +
"'"));
535 std::cout <<
"found dialog, calling user callback" << std::endl;
538 mxDialog->setAutoClose(mbAutoClose);
542 mCallback(*mxDialog);
546 mpException = std::current_exception();
554 virtual bool waitEndDialog(sal_uInt64 nTimeoutMs)
override
559 if (mbWaitingForDialog)
561 Timer aTimer(
"wait for dialog");
567 }
while (mbWaitingForDialog && aTimer.
IsActive());
571 std::rethrow_exception(mpException);
573 return !mbWaitingForDialog;
577 return std::make_shared<ListenerHelper>(
name, callback, bAutoClose);
static void SetDialogCancelMode(DialogCancelMode mode)
static css::uno::Reference< css::awt::XToolkit > GetVCLToolkit()
static DialogCancelMode GetDialogCancelMode()
static void ProcessEventsToIdle()
void SetPriority(TaskPriority ePriority)
void SetTimeout(sal_uInt64 nTimeoutMs)
void SetInvokeHandler(const Link< Timer *, void > &rLink)
virtual void Start(bool bStartTimer=true) override
void ClearInvokeHandler()
void setWindow(css::uno::Reference< css::accessibility::XAccessible > xAcc)
Sets the window on which post events based on an accessible object inside it.
Dialog(css::uno::Reference< css::awt::XDialog2 > &xDialog2, bool bAutoClose=true)
css::uno::Reference< css::accessibility::XAccessible > mxAccessible
void close(sal_Int32 result=VclResponseType::RET_CANCEL)
virtual void load(const rtl::OUString &sURL)
virtual void tearDown() override
static css::uno::Reference< css::accessibility::XAccessibleContext > getFocusedObject(const css::uno::Reference< css::accessibility::XAccessibleContext > &xCtx)
Gets the focused accessible object at xAcc level or below.
static bool isDocumentRole(const sal_Int16 role)
void dumpA11YTree(const css::uno::Reference< css::accessibility::XAccessibleContext > &xContext, const int depth=0)
Prints the tree of accessible objects starting at xContext to stdout.
bool activateMenuItem(const css::uno::Reference< css::accessibility::XAccessibleAction > &xAction)
static css::uno::Reference< css::accessibility::XAccessibleContext > getFirstRelationTargetOfType(const css::uno::Reference< css::accessibility::XAccessibleContext > &xContext, sal_Int16 relationType)
css::uno::Reference< css::accessibility::XAccessibleContext > getWindowAccessibleContext()
css::uno::Reference< css::frame::XDesktop2 > mxDesktop
virtual std::deque< css::uno::Reference< css::accessibility::XAccessibleContext > > getAllChildren(const css::uno::Reference< css::accessibility::XAccessibleContext > &xContext)
Tries to list all children of an accessible.
virtual void setUp() override
virtual void loadFromSrc(const rtl::OUString &sSrcPath)
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)
Navigates through focusable elements using the Tab keyboard shortcut.
static std::shared_ptr< DialogWaiter > awaitDialog(const std::u16string_view name, std::function< void(Dialog &)> callback, bool bAutoClose=true)
Helper to call user code when a given dialog opens.
virtual css::uno::Reference< css::accessibility::XAccessibleContext > getDocumentAccessibleContext()
css::uno::Reference< css::accessibility::XAccessibleContext > getItemFromName(const css::uno::Reference< css::accessibility::XAccessibleContext > &xMenuCtx, std::u16string_view name)
css::uno::Reference< css::uno::XComponentContext > mxComponentContext
virtual void setUp() override
#define LINK(Instance, Class, Member)
void SAL_CALL windowClosed(const css::lang::EventObject &e) override
void SAL_CALL windowMinimized(const css::lang::EventObject &e) override
void SAL_CALL windowDeactivated(const css::lang::EventObject &e) override
void SAL_CALL windowActivated(const css::lang::EventObject &e) override
DECL_LISTENERMULTIPLEXER_END void SAL_CALL windowOpened(const css::lang::EventObject &e) override
void SAL_CALL windowClosing(const css::lang::EventObject &e) override
void SAL_CALL windowNormalized(const css::lang::EventObject &e) override
Reference< XModel > xModel