1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include <QtGui/qtguiglobal.h>
41 #if QT_CONFIG(accessibility)
42
43 #include "qwindowsuiamainprovider.h"
44 #include "qwindowsuiavalueprovider.h"
45 #include "qwindowsuiarangevalueprovider.h"
46 #include "qwindowsuiatextprovider.h"
47 #include "qwindowsuiatoggleprovider.h"
48 #include "qwindowsuiainvokeprovider.h"
49 #include "qwindowsuiaselectionprovider.h"
50 #include "qwindowsuiaselectionitemprovider.h"
51 #include "qwindowsuiatableprovider.h"
52 #include "qwindowsuiatableitemprovider.h"
53 #include "qwindowsuiagridprovider.h"
54 #include "qwindowsuiagriditemprovider.h"
55 #include "qwindowsuiawindowprovider.h"
56 #include "qwindowsuiaexpandcollapseprovider.h"
57 #include "qwindowscombase.h"
58 #include "qwindowscontext.h"
59 #include "qwindowsuiautils.h"
60 #include "qwindowsuiaprovidercache.h"
61
62 #include <QtCore/qloggingcategory.h>
63 #include <QtGui/qaccessible.h>
64 #include <QtGui/qguiapplication.h>
65 #include <QtGui/qwindow.h>
66
67 #if !defined(Q_CC_BOR) && !defined (Q_CC_GNU)
68 #include <comdef.h>
69 #endif
70
71 #include <QtCore/qt_windows.h>
72
73 QT_BEGIN_NAMESPACE
74
75 using namespace QWindowsUiAutomation;
76
77
78 // Returns a cached instance of the provider for a specific acessible interface.
providerForAccessible(QAccessibleInterface * accessible)79 QWindowsUiaMainProvider *QWindowsUiaMainProvider::providerForAccessible(QAccessibleInterface *accessible)
80 {
81 if (!accessible)
82 return nullptr;
83
84 QAccessible::Id id = QAccessible::uniqueId(accessible);
85 QWindowsUiaProviderCache *providerCache = QWindowsUiaProviderCache::instance();
86 auto *provider = qobject_cast<QWindowsUiaMainProvider *>(providerCache->providerForId(id));
87
88 if (provider) {
89 provider->AddRef();
90 } else {
91 provider = new QWindowsUiaMainProvider(accessible);
92 providerCache->insert(id, provider);
93 }
94 return provider;
95 }
96
QWindowsUiaMainProvider(QAccessibleInterface * a,int initialRefCount)97 QWindowsUiaMainProvider::QWindowsUiaMainProvider(QAccessibleInterface *a, int initialRefCount)
98 : QWindowsUiaBaseProvider(QAccessible::uniqueId(a)),
99 m_ref(initialRefCount)
100 {
101 }
102
~QWindowsUiaMainProvider()103 QWindowsUiaMainProvider::~QWindowsUiaMainProvider()
104 {
105 }
106
notifyFocusChange(QAccessibleEvent * event)107 void QWindowsUiaMainProvider::notifyFocusChange(QAccessibleEvent *event)
108 {
109 if (QAccessibleInterface *accessible = event->accessibleInterface()) {
110 if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
111 QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_AutomationFocusChangedEventId);
112 }
113 }
114 }
115
notifyStateChange(QAccessibleStateChangeEvent * event)116 void QWindowsUiaMainProvider::notifyStateChange(QAccessibleStateChangeEvent *event)
117 {
118 if (QAccessibleInterface *accessible = event->accessibleInterface()) {
119 if (event->changedStates().checked || event->changedStates().checkStateMixed) {
120 // Notifies states changes in checkboxes.
121 if (accessible->role() == QAccessible::CheckBox) {
122 if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
123 VARIANT oldVal, newVal;
124 clearVariant(&oldVal);
125 int toggleState = ToggleState_Off;
126 if (accessible->state().checked)
127 toggleState = accessible->state().checkStateMixed ? ToggleState_Indeterminate : ToggleState_On;
128 setVariantI4(toggleState, &newVal);
129 QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider, UIA_ToggleToggleStatePropertyId, oldVal, newVal);
130 }
131 }
132 }
133 if (event->changedStates().active) {
134 if (accessible->role() == QAccessible::Window) {
135 // Notifies window opened/closed.
136 if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
137 if (accessible->state().active) {
138 QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Window_WindowOpenedEventId);
139 } else {
140 QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Window_WindowClosedEventId);
141 }
142 }
143 }
144 }
145 }
146 }
147
notifyValueChange(QAccessibleValueChangeEvent * event)148 void QWindowsUiaMainProvider::notifyValueChange(QAccessibleValueChangeEvent *event)
149 {
150 if (QAccessibleInterface *accessible = event->accessibleInterface()) {
151 if (accessible->role() == QAccessible::ComboBox && accessible->childCount() > 0) {
152 QAccessibleInterface *listacc = accessible->child(0);
153 if (listacc && listacc->role() == QAccessible::List) {
154 int count = listacc->childCount();
155 for (int i = 0; i < count; ++i) {
156 QAccessibleInterface *item = listacc->child(i);
157 if (item && item->isValid() && item->text(QAccessible::Name) == event->value()) {
158 if (!item->state().selected) {
159 if (QAccessibleActionInterface *actionInterface = item->actionInterface())
160 actionInterface->doAction(QAccessibleActionInterface::toggleAction());
161 }
162 break;
163 }
164 }
165 }
166 }
167 if (event->value().type() == QVariant::String) {
168 if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
169
170 // Tries to notify the change using UiaRaiseNotificationEvent(), which is only available on
171 // Windows 10 version 1709 or newer. Otherwise uses UiaRaiseAutomationPropertyChangedEvent().
172
173 BSTR displayString = bStrFromQString(event->value().toString());
174 BSTR activityId = bStrFromQString(QString());
175
176 HRESULT hr = QWindowsUiaWrapper::instance()->raiseNotificationEvent(provider, NotificationKind_Other,
177 NotificationProcessing_ImportantMostRecent,
178 displayString, activityId);
179
180 ::SysFreeString(displayString);
181 ::SysFreeString(activityId);
182
183 if (hr == static_cast<HRESULT>(UIA_E_NOTSUPPORTED)) {
184 VARIANT oldVal, newVal;
185 clearVariant(&oldVal);
186 setVariantString(event->value().toString(), &newVal);
187 QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider, UIA_ValueValuePropertyId, oldVal, newVal);
188 ::SysFreeString(newVal.bstrVal);
189 }
190 }
191 } else if (QAccessibleValueInterface *valueInterface = accessible->valueInterface()) {
192 if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
193 // Notifies changes in values of controls supporting the value interface.
194 VARIANT oldVal, newVal;
195 clearVariant(&oldVal);
196 setVariantDouble(valueInterface->currentValue().toDouble(), &newVal);
197 QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider, UIA_RangeValueValuePropertyId, oldVal, newVal);
198 }
199 }
200 }
201 }
202
notifyNameChange(QAccessibleEvent * event)203 void QWindowsUiaMainProvider::notifyNameChange(QAccessibleEvent *event)
204 {
205 if (QAccessibleInterface *accessible = event->accessibleInterface()) {
206 if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
207 VARIANT oldVal, newVal;
208 clearVariant(&oldVal);
209 setVariantString(accessible->text(QAccessible::Name), &newVal);
210 QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider, UIA_NamePropertyId, oldVal, newVal);
211 ::SysFreeString(newVal.bstrVal);
212 }
213 }
214 }
215
notifySelectionChange(QAccessibleEvent * event)216 void QWindowsUiaMainProvider::notifySelectionChange(QAccessibleEvent *event)
217 {
218 if (QAccessibleInterface *accessible = event->accessibleInterface()) {
219 if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
220 QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_SelectionItem_ElementSelectedEventId);
221 }
222 }
223 }
224
225 // Notifies changes in text content and selection state of text controls.
notifyTextChange(QAccessibleEvent * event)226 void QWindowsUiaMainProvider::notifyTextChange(QAccessibleEvent *event)
227 {
228 if (QAccessibleInterface *accessible = event->accessibleInterface()) {
229 if (accessible->textInterface()) {
230 if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
231 if (event->type() == QAccessible::TextSelectionChanged) {
232 QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Text_TextSelectionChangedEventId);
233 } else if (event->type() == QAccessible::TextCaretMoved) {
234 if (!accessible->state().readOnly) {
235 QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Text_TextSelectionChangedEventId);
236 }
237 } else {
238 QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Text_TextChangedEventId);
239 }
240 }
241 }
242 }
243 }
244
QueryInterface(REFIID iid,LPVOID * iface)245 HRESULT STDMETHODCALLTYPE QWindowsUiaMainProvider::QueryInterface(REFIID iid, LPVOID *iface)
246 {
247 if (!iface)
248 return E_INVALIDARG;
249 *iface = nullptr;
250
251 QAccessibleInterface *accessible = accessibleInterface();
252
253 const bool result = qWindowsComQueryUnknownInterfaceMulti<IRawElementProviderSimple>(this, iid, iface)
254 || qWindowsComQueryInterface<IRawElementProviderSimple>(this, iid, iface)
255 || qWindowsComQueryInterface<IRawElementProviderFragment>(this, iid, iface)
256 || (accessible && hwndForAccessible(accessible) && qWindowsComQueryInterface<IRawElementProviderFragmentRoot>(this, iid, iface));
257 return result ? S_OK : E_NOINTERFACE;
258 }
259
AddRef()260 ULONG QWindowsUiaMainProvider::AddRef()
261 {
262 return ++m_ref;
263 }
264
Release()265 ULONG STDMETHODCALLTYPE QWindowsUiaMainProvider::Release()
266 {
267 if (!--m_ref) {
268 delete this;
269 return 0;
270 }
271 return m_ref;
272 }
273
get_ProviderOptions(ProviderOptions * pRetVal)274 HRESULT QWindowsUiaMainProvider::get_ProviderOptions(ProviderOptions *pRetVal)
275 {
276 if (!pRetVal)
277 return E_INVALIDARG;
278 // We are STA, (OleInitialize()).
279 *pRetVal = static_cast<ProviderOptions>(ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading);
280 return S_OK;
281 }
282
283 // Return providers for specific control patterns
GetPatternProvider(PATTERNID idPattern,IUnknown ** pRetVal)284 HRESULT QWindowsUiaMainProvider::GetPatternProvider(PATTERNID idPattern, IUnknown **pRetVal)
285 {
286 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << idPattern;
287
288 if (!pRetVal)
289 return E_INVALIDARG;
290 *pRetVal = nullptr;
291
292 QAccessibleInterface *accessible = accessibleInterface();
293 if (!accessible)
294 return UIA_E_ELEMENTNOTAVAILABLE;
295
296 switch (idPattern) {
297 case UIA_WindowPatternId:
298 if (accessible->parent() && (accessible->parent()->role() == QAccessible::Application)) {
299 *pRetVal = new QWindowsUiaWindowProvider(id());
300 }
301 break;
302 case UIA_TextPatternId:
303 case UIA_TextPattern2Id:
304 // All text controls.
305 if (accessible->textInterface()) {
306 *pRetVal = new QWindowsUiaTextProvider(id());
307 }
308 break;
309 case UIA_ValuePatternId:
310 // All non-static controls support the Value pattern.
311 if (accessible->role() != QAccessible::StaticText)
312 *pRetVal = new QWindowsUiaValueProvider(id());
313 break;
314 case UIA_RangeValuePatternId:
315 // Controls providing a numeric value within a range (e.g., sliders, scroll bars, dials).
316 if (accessible->valueInterface()) {
317 *pRetVal = new QWindowsUiaRangeValueProvider(id());
318 }
319 break;
320 case UIA_TogglePatternId:
321 // Checkboxes and other checkable controls.
322 if (accessible->state().checkable)
323 *pRetVal = new QWindowsUiaToggleProvider(id());
324 break;
325 case UIA_SelectionPatternId:
326 // Lists of items.
327 if (accessible->role() == QAccessible::List) {
328 *pRetVal = new QWindowsUiaSelectionProvider(id());
329 }
330 break;
331 case UIA_SelectionItemPatternId:
332 // Items within a list and radio buttons.
333 if ((accessible->role() == QAccessible::RadioButton)
334 || (accessible->role() == QAccessible::ListItem)) {
335 *pRetVal = new QWindowsUiaSelectionItemProvider(id());
336 }
337 break;
338 case UIA_TablePatternId:
339 // Table/tree.
340 if (accessible->tableInterface()
341 && ((accessible->role() == QAccessible::Table) || (accessible->role() == QAccessible::Tree))) {
342 *pRetVal = new QWindowsUiaTableProvider(id());
343 }
344 break;
345 case UIA_TableItemPatternId:
346 // Item within a table/tree.
347 if (accessible->tableCellInterface()
348 && ((accessible->role() == QAccessible::Cell) || (accessible->role() == QAccessible::TreeItem))) {
349 *pRetVal = new QWindowsUiaTableItemProvider(id());
350 }
351 break;
352 case UIA_GridPatternId:
353 // Table/tree.
354 if (accessible->tableInterface()
355 && ((accessible->role() == QAccessible::Table) || (accessible->role() == QAccessible::Tree))) {
356 *pRetVal = new QWindowsUiaGridProvider(id());
357 }
358 break;
359 case UIA_GridItemPatternId:
360 // Item within a table/tree.
361 if (accessible->tableCellInterface()
362 && ((accessible->role() == QAccessible::Cell) || (accessible->role() == QAccessible::TreeItem))) {
363 *pRetVal = new QWindowsUiaGridItemProvider(id());
364 }
365 break;
366 case UIA_InvokePatternId:
367 // Things that have an invokable action (e.g., simple buttons).
368 if (accessible->actionInterface()) {
369 *pRetVal = new QWindowsUiaInvokeProvider(id());
370 }
371 break;
372 case UIA_ExpandCollapsePatternId:
373 // Menu items with submenus.
374 if (accessible->role() == QAccessible::MenuItem
375 && accessible->childCount() > 0
376 && accessible->child(0)->role() == QAccessible::PopupMenu) {
377 *pRetVal = new QWindowsUiaExpandCollapseProvider(id());
378 }
379 break;
380 default:
381 break;
382 }
383
384 return S_OK;
385 }
386
GetPropertyValue(PROPERTYID idProp,VARIANT * pRetVal)387 HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pRetVal)
388 {
389 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << idProp;
390
391 if (!pRetVal)
392 return E_INVALIDARG;
393 clearVariant(pRetVal);
394
395 QAccessibleInterface *accessible = accessibleInterface();
396 if (!accessible)
397 return UIA_E_ELEMENTNOTAVAILABLE;
398
399 bool topLevelWindow = accessible->parent() && (accessible->parent()->role() == QAccessible::Application);
400
401 switch (idProp) {
402 case UIA_ProcessIdPropertyId:
403 // PID
404 setVariantI4(int(GetCurrentProcessId()), pRetVal);
405 break;
406 case UIA_AccessKeyPropertyId:
407 // Accelerator key.
408 setVariantString(accessible->text(QAccessible::Accelerator), pRetVal);
409 break;
410 case UIA_AutomationIdPropertyId:
411 // Automation ID, which can be used by tools to select a specific control in the UI.
412 setVariantString(automationIdForAccessible(accessible), pRetVal);
413 break;
414 case UIA_ClassNamePropertyId:
415 // Class name.
416 if (QObject *o = accessible->object()) {
417 QString className = QLatin1String(o->metaObject()->className());
418 setVariantString(className, pRetVal);
419 }
420 break;
421 case UIA_FrameworkIdPropertyId:
422 setVariantString(QStringLiteral("Qt"), pRetVal);
423 break;
424 case UIA_ControlTypePropertyId:
425 if (topLevelWindow) {
426 // Reports a top-level widget as a window, instead of "custom".
427 setVariantI4(UIA_WindowControlTypeId, pRetVal);
428 } else {
429 // Control type converted from role.
430 auto controlType = roleToControlTypeId(accessible->role());
431
432 // The native OSK should be disbled if the Qt OSK is in use,
433 // or if disabled via application attribute.
434 static bool imModuleEmpty = qEnvironmentVariableIsEmpty("QT_IM_MODULE");
435 bool nativeVKDisabled = QCoreApplication::testAttribute(Qt::AA_DisableNativeVirtualKeyboard);
436
437 // If we want to disable the native OSK auto-showing
438 // we have to report text fields as non-editable.
439 if (controlType == UIA_EditControlTypeId && (!imModuleEmpty || nativeVKDisabled))
440 controlType = UIA_TextControlTypeId;
441
442 setVariantI4(controlType, pRetVal);
443 }
444 break;
445 case UIA_HelpTextPropertyId:
446 setVariantString(accessible->text(QAccessible::Help), pRetVal);
447 break;
448 case UIA_HasKeyboardFocusPropertyId:
449 if (topLevelWindow) {
450 // Windows set the active state to true when they are focused
451 setVariantBool(accessible->state().active, pRetVal);
452 } else {
453 setVariantBool(accessible->state().focused, pRetVal);
454 }
455 break;
456 case UIA_IsKeyboardFocusablePropertyId:
457 if (topLevelWindow) {
458 // Windows should always be focusable
459 setVariantBool(true, pRetVal);
460 } else {
461 setVariantBool(accessible->state().focusable, pRetVal);
462 }
463 break;
464 case UIA_IsOffscreenPropertyId:
465 setVariantBool(accessible->state().offscreen, pRetVal);
466 break;
467 case UIA_IsContentElementPropertyId:
468 setVariantBool(true, pRetVal);
469 break;
470 case UIA_IsControlElementPropertyId:
471 setVariantBool(true, pRetVal);
472 break;
473 case UIA_IsEnabledPropertyId:
474 setVariantBool(!accessible->state().disabled, pRetVal);
475 break;
476 case UIA_IsPasswordPropertyId:
477 setVariantBool(accessible->role() == QAccessible::EditableText
478 && accessible->state().passwordEdit, pRetVal);
479 break;
480 case UIA_IsPeripheralPropertyId:
481 // True for peripheral UIs.
482 if (QWindow *window = windowForAccessible(accessible)) {
483 const Qt::WindowType wt = window->type();
484 setVariantBool(wt == Qt::Popup || wt == Qt::ToolTip || wt == Qt::SplashScreen, pRetVal);
485 }
486 break;
487 case UIA_IsDialogPropertyId:
488 setVariantBool(accessible->role() == QAccessible::Dialog
489 || accessible->role() == QAccessible::AlertMessage, pRetVal);
490 break;
491 case UIA_FullDescriptionPropertyId:
492 setVariantString(accessible->text(QAccessible::Description), pRetVal);
493 break;
494 case UIA_NamePropertyId: {
495 QString name = accessible->text(QAccessible::Name);
496 if (name.isEmpty() && topLevelWindow)
497 name = QCoreApplication::applicationName();
498 setVariantString(name, pRetVal);
499 break;
500 }
501 default:
502 break;
503 }
504 return S_OK;
505 }
506
507 // Generates an ID based on the name of the controls and their parents.
automationIdForAccessible(const QAccessibleInterface * accessible)508 QString QWindowsUiaMainProvider::automationIdForAccessible(const QAccessibleInterface *accessible)
509 {
510 QString result;
511 if (accessible) {
512 QObject *obj = accessible->object();
513 while (obj) {
514 QString name = obj->objectName();
515 if (name.isEmpty())
516 return QString();
517 if (!result.isEmpty())
518 result.prepend(u'.');
519 result.prepend(name);
520 obj = obj->parent();
521 }
522 }
523 return result;
524 }
525
get_HostRawElementProvider(IRawElementProviderSimple ** pRetVal)526 HRESULT QWindowsUiaMainProvider::get_HostRawElementProvider(IRawElementProviderSimple **pRetVal)
527 {
528 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
529
530 if (!pRetVal)
531 return E_INVALIDARG;
532 *pRetVal = nullptr;
533
534 // Returns a host provider only for controls associated with a native window handle. Others should return NULL.
535 if (QAccessibleInterface *accessible = accessibleInterface()) {
536 if (HWND hwnd = hwndForAccessible(accessible)) {
537 return QWindowsUiaWrapper::instance()->hostProviderFromHwnd(hwnd, pRetVal);
538 }
539 }
540 return S_OK;
541 }
542
543 // Navigates within the tree of accessible controls.
Navigate(NavigateDirection direction,IRawElementProviderFragment ** pRetVal)544 HRESULT QWindowsUiaMainProvider::Navigate(NavigateDirection direction, IRawElementProviderFragment **pRetVal)
545 {
546 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << direction << " this: " << this;
547
548 if (!pRetVal)
549 return E_INVALIDARG;
550 *pRetVal = nullptr;
551
552 QAccessibleInterface *accessible = accessibleInterface();
553 if (!accessible)
554 return UIA_E_ELEMENTNOTAVAILABLE;
555
556 QAccessibleInterface *targetacc = nullptr;
557
558 if (direction == NavigateDirection_Parent) {
559 if (QAccessibleInterface *parent = accessible->parent()) {
560 // The Application's children are considered top level objects.
561 if (parent->isValid() && parent->role() != QAccessible::Application) {
562 targetacc = parent;
563 }
564 }
565 } else {
566 QAccessibleInterface *parent = nullptr;
567 int index = 0;
568 int incr = 1;
569 switch (direction) {
570 case NavigateDirection_FirstChild:
571 parent = accessible;
572 index = 0;
573 incr = 1;
574 break;
575 case NavigateDirection_LastChild:
576 parent = accessible;
577 index = accessible->childCount() - 1;
578 incr = -1;
579 break;
580 case NavigateDirection_NextSibling:
581 if ((parent = accessible->parent()))
582 index = parent->indexOfChild(accessible) + 1;
583 incr = 1;
584 break;
585 case NavigateDirection_PreviousSibling:
586 if ((parent = accessible->parent()))
587 index = parent->indexOfChild(accessible) - 1;
588 incr = -1;
589 break;
590 default:
591 Q_UNREACHABLE();
592 break;
593 }
594
595 if (parent && parent->isValid()) {
596 for (int count = parent->childCount(); index >= 0 && index < count; index += incr) {
597 if (QAccessibleInterface *child = parent->child(index)) {
598 if (child->isValid() && !child->state().invisible) {
599 targetacc = child;
600 break;
601 }
602 }
603 }
604 }
605 }
606
607 if (targetacc)
608 *pRetVal = providerForAccessible(targetacc);
609 return S_OK;
610 }
611
612 // Returns a unique id assigned to the UI element, used as key by the UI Automation framework.
GetRuntimeId(SAFEARRAY ** pRetVal)613 HRESULT QWindowsUiaMainProvider::GetRuntimeId(SAFEARRAY **pRetVal)
614 {
615 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
616
617 if (!pRetVal)
618 return E_INVALIDARG;
619 *pRetVal = nullptr;
620
621 QAccessibleInterface *accessible = accessibleInterface();
622 if (!accessible)
623 return UIA_E_ELEMENTNOTAVAILABLE;
624
625 // The UiaAppendRuntimeId constant is used to make then ID unique
626 // among multiple instances running on the system.
627 int rtId[] = { UiaAppendRuntimeId, int(id()) };
628
629 if ((*pRetVal = SafeArrayCreateVector(VT_I4, 0, 2))) {
630 for (LONG i = 0; i < 2; ++i)
631 SafeArrayPutElement(*pRetVal, &i, &rtId[i]);
632 }
633 return S_OK;
634 }
635
636 // Returns the bounding rectangle for the accessible control.
get_BoundingRectangle(UiaRect * pRetVal)637 HRESULT QWindowsUiaMainProvider::get_BoundingRectangle(UiaRect *pRetVal)
638 {
639 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
640
641 if (!pRetVal)
642 return E_INVALIDARG;
643
644 QAccessibleInterface *accessible = accessibleInterface();
645 if (!accessible)
646 return UIA_E_ELEMENTNOTAVAILABLE;
647
648 QWindow *window = windowForAccessible(accessible);
649 if (!window)
650 return UIA_E_ELEMENTNOTAVAILABLE;
651
652 rectToNativeUiaRect(accessible->rect(), window, pRetVal);
653 return S_OK;
654 }
655
GetEmbeddedFragmentRoots(SAFEARRAY ** pRetVal)656 HRESULT QWindowsUiaMainProvider::GetEmbeddedFragmentRoots(SAFEARRAY **pRetVal)
657 {
658 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
659
660 if (!pRetVal)
661 return E_INVALIDARG;
662 *pRetVal = nullptr;
663 // No embedded roots.
664 return S_OK;
665 }
666
667 // Sets focus to the control.
SetFocus()668 HRESULT QWindowsUiaMainProvider::SetFocus()
669 {
670 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
671
672 QAccessibleInterface *accessible = accessibleInterface();
673 if (!accessible)
674 return UIA_E_ELEMENTNOTAVAILABLE;
675
676 QAccessibleActionInterface *actionInterface = accessible->actionInterface();
677 if (!actionInterface)
678 return UIA_E_ELEMENTNOTAVAILABLE;
679
680 actionInterface->doAction(QAccessibleActionInterface::setFocusAction());
681 return S_OK;
682 }
683
get_FragmentRoot(IRawElementProviderFragmentRoot ** pRetVal)684 HRESULT QWindowsUiaMainProvider::get_FragmentRoot(IRawElementProviderFragmentRoot **pRetVal)
685 {
686 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
687
688 if (!pRetVal)
689 return E_INVALIDARG;
690 *pRetVal = nullptr;
691
692 // Our UI Automation implementation considers the window as the root for
693 // non-native controls/fragments.
694 if (QAccessibleInterface *accessible = accessibleInterface()) {
695 if (QWindow *window = windowForAccessible(accessible)) {
696 if (QAccessibleInterface *rootacc = window->accessibleRoot()) {
697 *pRetVal = providerForAccessible(rootacc);
698 }
699 }
700 }
701 return S_OK;
702 }
703
704 // Returns a provider for the UI element present at the specified screen coordinates.
ElementProviderFromPoint(double x,double y,IRawElementProviderFragment ** pRetVal)705 HRESULT QWindowsUiaMainProvider::ElementProviderFromPoint(double x, double y, IRawElementProviderFragment **pRetVal)
706 {
707 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << x << y;
708
709 if (!pRetVal) {
710 return E_INVALIDARG;
711 }
712 *pRetVal = nullptr;
713
714 QAccessibleInterface *accessible = accessibleInterface();
715 if (!accessible)
716 return UIA_E_ELEMENTNOTAVAILABLE;
717
718 QWindow *window = windowForAccessible(accessible);
719 if (!window)
720 return UIA_E_ELEMENTNOTAVAILABLE;
721
722 // Scales coordinates from High DPI screens.
723 UiaPoint uiaPoint = {x, y};
724 QPoint point;
725 nativeUiaPointToPoint(uiaPoint, window, &point);
726
727 if (auto targetacc = accessible->childAt(point.x(), point.y())) {
728 auto acc = accessible->childAt(point.x(), point.y());
729 // Reject the cases where childAt() returns a different instance in each call for the same
730 // element (e.g., QAccessibleTree), as it causes an endless loop with Youdao Dictionary installed.
731 if (targetacc == acc) {
732 // Controls can be embedded within grouping elements. By default returns the innermost control.
733 while (acc) {
734 targetacc = acc;
735 // For accessibility tools it may be better to return the text element instead of its subcomponents.
736 if (targetacc->textInterface()) break;
737 acc = targetacc->childAt(point.x(), point.y());
738 if (acc != targetacc->childAt(point.x(), point.y())) {
739 qCDebug(lcQpaUiAutomation) << "Non-unique childAt() for" << targetacc;
740 break;
741 }
742 }
743 *pRetVal = providerForAccessible(targetacc);
744 } else {
745 qCDebug(lcQpaUiAutomation) << "Non-unique childAt() for" << accessible;
746 }
747 }
748 return S_OK;
749 }
750
751 // Returns the fragment with focus.
GetFocus(IRawElementProviderFragment ** pRetVal)752 HRESULT QWindowsUiaMainProvider::GetFocus(IRawElementProviderFragment **pRetVal)
753 {
754 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
755
756 if (!pRetVal)
757 return E_INVALIDARG;
758 *pRetVal = nullptr;
759
760 if (QAccessibleInterface *accessible = accessibleInterface()) {
761 if (QAccessibleInterface *focusacc = accessible->focusChild()) {
762 *pRetVal = providerForAccessible(focusacc);
763 }
764 }
765 return S_OK;
766 }
767
768 QT_END_NAMESPACE
769
770 #endif // QT_CONFIG(accessibility)
771