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