1 /****************************************************************************
2 **
3 ** Copyright (C) 2018 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 "qwinrtuiamainprovider.h"
44 #include "qwinrtuiaprovidercache.h"
45 #include "qwinrtuiavalueprovider.h"
46 #include "qwinrtuiarangevalueprovider.h"
47 #include "qwinrtuiatextprovider.h"
48 #include "qwinrtuiatoggleprovider.h"
49 #include "qwinrtuiainvokeprovider.h"
50 #include "qwinrtuiaselectionprovider.h"
51 #include "qwinrtuiaselectionitemprovider.h"
52 #include "qwinrtuiatableprovider.h"
53 #include "qwinrtuiatableitemprovider.h"
54 #include "qwinrtuiagridprovider.h"
55 #include "qwinrtuiagriditemprovider.h"
56 #include "qwinrtuiapeervector.h"
57 #include "qwinrtuiametadatacache.h"
58 #include "qwinrtuiaemptypropertyvalue.h"
59 #include "qwinrtuiautils.h"
60 
61 #include <QCoreApplication>
62 #include <QSemaphore>
63 #include <QtCore/QLoggingCategory>
64 #include <QtCore/qfunctions_winrt.h>
65 #include <QtCore/private/qeventdispatcher_winrt_p.h>
66 
67 #include <memory>
68 
69 using namespace QWinRTUiAutomation;
70 using namespace Microsoft::WRL;
71 using namespace Microsoft::WRL::Wrappers;
72 using namespace ABI::Windows::System;
73 using namespace ABI::Windows::UI;
74 using namespace ABI::Windows::UI::Core;
75 using namespace ABI::Windows::UI::Xaml;
76 using namespace ABI::Windows::UI::Xaml::Automation;
77 using namespace ABI::Windows::UI::Xaml::Automation::Provider;
78 using namespace ABI::Windows::UI::Xaml::Automation::Peers;
79 using namespace ABI::Windows::Foundation;
80 using namespace ABI::Windows::Foundation::Collections;
81 
82 QT_BEGIN_NAMESPACE
83 
QWinRTUiaMainProvider(QAccessible::Id id)84 QWinRTUiaMainProvider::QWinRTUiaMainProvider(QAccessible::Id id)
85     : QWinRTUiaBaseProvider(id)
86 {
87     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
88 
89     ComPtr<IAutomationPeerFactory> factory;
90     HRESULT hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_Xaml_Automation_Peers_AutomationPeer).Get(), IID_PPV_ARGS(&factory));
91     Q_ASSERT_SUCCEEDED(hr);
92 
93     hr = factory->CreateInstance(this, &m_base, &m_core);
94     Q_ASSERT_SUCCEEDED(hr);
95 }
96 
~QWinRTUiaMainProvider()97 QWinRTUiaMainProvider::~QWinRTUiaMainProvider()
98 {
99     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
100 }
101 
QueryInterface(REFIID iid,LPVOID * iface)102 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::QueryInterface(REFIID iid, LPVOID *iface)
103 {
104     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
105 
106     if (!iface)
107         return E_POINTER;
108     *iface = nullptr;
109 
110     if (iid == IID_IUnknown) {
111         *iface = static_cast<IAutomationPeerOverrides *>(this);
112         AddRef();
113         return S_OK;
114     } else if (iid == IID_IAutomationPeerOverrides) {
115         *iface = static_cast<IAutomationPeerOverrides *>(this);
116         AddRef();
117         return S_OK;
118     } else {
119         return m_base.CopyTo(iid, iface);
120     }
121 }
122 
GetIids(ULONG * iidCount,IID ** iids)123 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::GetIids(ULONG *iidCount, IID **iids)
124 {
125     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
126     *iidCount = 0;
127     *iids = nullptr;
128     return S_OK;
129 }
130 
GetRuntimeClassName(HSTRING * className)131 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::GetRuntimeClassName(HSTRING *className)
132 {
133     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
134     return qHString(QStringLiteral("QWinRTUiaMainProvider"), className);
135 }
136 
GetTrustLevel(TrustLevel * trustLevel)137 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::GetTrustLevel(TrustLevel *trustLevel)
138 {
139     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
140     *trustLevel = TrustLevel::BaseTrust;
141     return S_OK;
142 }
143 
144 // Returns a cached instance of the provider for a specific accessible interface.
providerForAccessibleId(QAccessible::Id id)145 QWinRTUiaMainProvider *QWinRTUiaMainProvider::providerForAccessibleId(QAccessible::Id id)
146 {
147     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
148 
149     QWinRTUiaProviderCache *providerCache = QWinRTUiaProviderCache::instance();
150     QWinRTUiaMainProvider *provider = qobject_cast<QWinRTUiaMainProvider *>(providerCache->providerForId(id));
151 
152     if (provider) {
153         provider->AddRef();
154     } else {
155         ComPtr<QWinRTUiaMainProvider> p = Make<QWinRTUiaMainProvider>(id);
156         provider = p.Get();
157         provider->AddRef();
158         providerCache->insert(id, provider);
159     }
160     return provider;
161 }
162 
163 // Returns an IIRawElementProviderSimple for a specific accessible interface.
rawProviderForAccessibleId(QAccessible::Id elementId,IIRawElementProviderSimple ** returnValue)164 HRESULT QWinRTUiaMainProvider::rawProviderForAccessibleId(QAccessible::Id elementId,
165                                                           IIRawElementProviderSimple **returnValue)
166 {
167     if (!returnValue)
168         return E_INVALIDARG;
169     *returnValue = nullptr;
170 
171     if (ComPtr<QWinRTUiaMainProvider> provider = providerForAccessibleId(elementId)) {
172         ComPtr<IAutomationPeer> automationPeer;
173         if (SUCCEEDED(provider.As(&automationPeer))) {
174             ComPtr<IAutomationPeerProtected> automationPeerProtected;
175             if (SUCCEEDED(provider.As(&automationPeerProtected))) {
176                 return automationPeerProtected->ProviderFromPeer(automationPeer.Get(), returnValue);
177             }
178         }
179     }
180     return E_FAIL;
181 }
182 
183 // Returns an array of IIRawElementProviderSimple instances for a list of accessible interface ids.
rawProviderArrayForAccessibleIdList(const QVarLengthArray<QAccessible::Id> & elementIds,UINT32 * returnValueSize,IIRawElementProviderSimple *** returnValue)184 HRESULT QWinRTUiaMainProvider::rawProviderArrayForAccessibleIdList(const QVarLengthArray<QAccessible::Id> &elementIds,
185                                                                    UINT32 *returnValueSize,
186                                                                    IIRawElementProviderSimple ***returnValue)
187 {
188     if (!returnValueSize || !returnValue)
189         return E_INVALIDARG;
190     *returnValueSize = 0;
191     *returnValue = nullptr;
192 
193     QList<IIRawElementProviderSimple *> rawProviderList;
194 
195     for (auto elementId : elementIds) {
196         IIRawElementProviderSimple *rawProvider;
197         if (SUCCEEDED(rawProviderForAccessibleId(elementId, &rawProvider)))
198             rawProviderList.append(rawProvider);
199     }
200 
201     if (rawProviderList.size() == 0)
202         return S_OK;
203 
204     *returnValue = static_cast<IIRawElementProviderSimple **>(CoTaskMemAlloc(rawProviderList.size() * sizeof(IIRawElementProviderSimple *)));
205     if (!*returnValue) {
206         for (auto rawProvider : qAsConst(rawProviderList))
207             rawProvider->Release();
208         return E_OUTOFMEMORY;
209     }
210 
211     int index = 0;
212     for (auto rawProvider : qAsConst(rawProviderList))
213         (*returnValue)[index++] = rawProvider;
214     *returnValueSize = rawProviderList.size();
215     return S_OK;
216 }
217 
notifyFocusChange(QAccessibleEvent * event)218 void QWinRTUiaMainProvider::notifyFocusChange(QAccessibleEvent *event)
219 {
220     if (QAccessibleInterface *accessible = event->accessibleInterface()) {
221         QAccessible::Id accid = idForAccessible(accessible);
222         QWinRTUiaMetadataCache::instance()->load(accid);
223         QEventDispatcherWinRT::runOnXamlThread([accid]() {
224             if (ComPtr<QWinRTUiaMainProvider> provider = providerForAccessibleId(accid)) {
225                 ComPtr<IAutomationPeer> automationPeer;
226                 if (SUCCEEDED(provider->QueryInterface(IID_PPV_ARGS(&automationPeer)))) {
227                     automationPeer->RaiseAutomationEvent(AutomationEvents_AutomationFocusChanged);
228                 }
229             }
230             return S_OK;
231         }, false);
232     }
233 }
234 
notifyVisibilityChange(QAccessibleEvent * event)235 void QWinRTUiaMainProvider::notifyVisibilityChange(QAccessibleEvent *event)
236 {
237     if (QAccessibleInterface *accessible = event->accessibleInterface()) {
238         QAccessible::Id accid = idForAccessible(accessible);
239         QWinRTUiaMetadataCache::instance()->load(accid);
240     }
241 }
242 
notifyStateChange(QAccessibleStateChangeEvent * event)243 void QWinRTUiaMainProvider::notifyStateChange(QAccessibleStateChangeEvent *event)
244 {
245     if (QAccessibleInterface *accessible = event->accessibleInterface()) {
246         QAccessible::Id accid = idForAccessible(accessible);
247         QWinRTUiaMetadataCache::instance()->load(accid);
248 
249         if (event->changedStates().checked || event->changedStates().checkStateMixed) {
250             // Notifies states changes in checkboxes.
251             if (accessible->role() == QAccessible::CheckBox) {
252                 QEventDispatcherWinRT::runOnXamlThread([accid]() {
253                     if (ComPtr<QWinRTUiaMainProvider> provider = providerForAccessibleId(accid)) {
254                         ComPtr<IAutomationPeer> automationPeer;
255                         if (SUCCEEDED(provider->QueryInterface(IID_PPV_ARGS(&automationPeer)))) {
256                             ComPtr<ITogglePatternIdentifiersStatics> toggleStatics;
257                             if (SUCCEEDED(RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_Xaml_Automation_TogglePatternIdentifiers).Get(), IID_PPV_ARGS(&toggleStatics)))) {
258                                 ComPtr<IAutomationProperty> toggleStateProperty;
259                                 if (SUCCEEDED(toggleStatics->get_ToggleStateProperty(&toggleStateProperty))) {
260                                     ComPtr<QWinRTUiaEmptyPropertyValue> emptyValue = Make<QWinRTUiaEmptyPropertyValue>();
261                                     // by sending an event with an empty value we force ui automation to refresh its state
262                                     automationPeer->RaisePropertyChangedEvent(toggleStateProperty.Get(), emptyValue.Get(), emptyValue.Get());
263                                 }
264                             }
265                         }
266                     }
267                     return S_OK;
268                 }, false);
269             }
270         }
271         if (event->changedStates().active) {
272             if (accessible->role() == QAccessible::Window) {
273                 // Notifies window opened/closed.
274                 bool active = accessible->state().active;
275                 QEventDispatcherWinRT::runOnXamlThread([accid, active]() {
276                     if (ComPtr<QWinRTUiaMainProvider> provider = providerForAccessibleId(accid)) {
277                         ComPtr<IAutomationPeer> automationPeer;
278                         if (SUCCEEDED(provider->QueryInterface(IID_PPV_ARGS(&automationPeer)))) {
279                             if (active) {
280                                 automationPeer->RaiseAutomationEvent(AutomationEvents_WindowOpened);
281                             } else {
282                                 automationPeer->RaiseAutomationEvent(AutomationEvents_WindowClosed);
283                             }
284                         }
285                     }
286                     return S_OK;
287                 }, false);
288             }
289         }
290     }
291 }
292 
notifyValueChange(QAccessibleValueChangeEvent * event)293 void QWinRTUiaMainProvider::notifyValueChange(QAccessibleValueChangeEvent *event)
294 {
295     if (QAccessibleInterface *accessible = event->accessibleInterface()) {
296         QAccessible::Id accid = idForAccessible(accessible);
297         QWinRTUiaMetadataCache::instance()->load(accid);
298         if (QAccessibleValueInterface *valueInterface = accessible->valueInterface()) {
299             // Notifies changes in values of controls supporting the value interface.
300             double value = valueInterface->currentValue().toDouble();
301             QEventDispatcherWinRT::runOnXamlThread([accid, value]() {
302                 // For some reason RaisePropertyChangedEvent() does not seem to be
303                 // forwarding notifications for any property types except empty,
304                 // which would do nothing here. ToDo: find a workaround.
305                 return S_OK;
306             }, false);
307         }
308     }
309 }
310 
311 // Notifies changes in text content and selection state of text controls.
notifyTextChange(QAccessibleEvent * event)312 void QWinRTUiaMainProvider::notifyTextChange(QAccessibleEvent *event)
313 {
314     if (QAccessibleInterface *accessible = event->accessibleInterface()) {
315         QAccessible::Id accid = idForAccessible(accessible);
316         QWinRTUiaMetadataCache::instance()->load(accid);
317         bool readOnly = accessible->state().readOnly;
318         QAccessible::Event eventType = event->type();
319         if (accessible->textInterface()) {
320             QEventDispatcherWinRT::runOnXamlThread([accid, eventType, readOnly]() {
321                 if (ComPtr<QWinRTUiaMainProvider> provider = providerForAccessibleId(accid)) {
322                     ComPtr<IAutomationPeer> automationPeer;
323                     if (SUCCEEDED(provider->QueryInterface(IID_PPV_ARGS(&automationPeer)))) {
324                         if (eventType == QAccessible::TextSelectionChanged) {
325                             automationPeer->RaiseAutomationEvent(AutomationEvents_TextPatternOnTextSelectionChanged);
326                         } else if (eventType == QAccessible::TextCaretMoved) {
327                             if (!readOnly) {
328                                 automationPeer->RaiseAutomationEvent(AutomationEvents_TextPatternOnTextSelectionChanged);
329                             }
330                         } else {
331                             automationPeer->RaiseAutomationEvent(AutomationEvents_TextPatternOnTextChanged);
332                         }
333                     }
334                 }
335                 return S_OK;
336             }, false);
337         }
338     }
339 }
340 
341 // Return providers for specific control patterns
GetPatternCore(PatternInterface patternInterface,IInspectable ** returnValue)342 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::GetPatternCore(PatternInterface patternInterface, IInspectable **returnValue)
343 {
344     qCDebug(lcQpaUiAutomation) << __FUNCTION__ << patternInterface;
345 
346     if (!returnValue)
347         return E_INVALIDARG;
348     *returnValue = nullptr;
349 
350     QAccessibleInterface *accessible = accessibleInterface();
351     if (!accessible)
352         return E_FAIL;
353 
354     switch (patternInterface) {
355     case PatternInterface_Text:
356     case PatternInterface_Text2: {
357         // All text controls.
358         if (accessible->textInterface()) {
359             ComPtr<QWinRTUiaTextProvider> provider = Make<QWinRTUiaTextProvider>(id());
360             return provider.CopyTo(returnValue);
361         }
362         break;
363     }
364     case PatternInterface_Value: {
365         // All accessible controls return text(QAccessible::Value) (which may be empty).
366         ComPtr<QWinRTUiaValueProvider> provider = Make<QWinRTUiaValueProvider>(id());
367         return provider.CopyTo(returnValue);
368     }
369     case PatternInterface_RangeValue: {
370         // Controls providing a numeric value within a range (e.g., sliders, scroll bars, dials).
371         if (accessible->valueInterface()) {
372             ComPtr<QWinRTUiaRangeValueProvider> provider = Make<QWinRTUiaRangeValueProvider>(id());
373             return provider.CopyTo(returnValue);
374         }
375         break;
376     }
377     case PatternInterface_Toggle: {
378         // Checkbox controls.
379         if (accessible->role() == QAccessible::CheckBox) {
380             ComPtr<QWinRTUiaToggleProvider> provider = Make<QWinRTUiaToggleProvider>(id());
381             return provider.CopyTo(returnValue);
382         }
383         break;
384     }
385     case PatternInterface_Selection: {
386         // Lists of items.
387         if (accessible->role() == QAccessible::List) {
388             ComPtr<QWinRTUiaSelectionProvider> provider = Make<QWinRTUiaSelectionProvider>(id());
389             return provider.CopyTo(returnValue);
390         }
391         break;
392     }
393     case PatternInterface_SelectionItem: {
394         // Items within a list and radio buttons.
395         if ((accessible->role() == QAccessible::RadioButton)
396                 || (accessible->role() == QAccessible::ListItem)) {
397             ComPtr<QWinRTUiaSelectionItemProvider> provider = Make<QWinRTUiaSelectionItemProvider>(id());
398             return provider.CopyTo(returnValue);
399         }
400         break;
401     }
402     case PatternInterface_Table: {
403         // Table/tree.
404         if (accessible->tableInterface()
405                 && ((accessible->role() == QAccessible::Table) || (accessible->role() == QAccessible::Tree))) {
406             ComPtr<QWinRTUiaTableProvider> provider = Make<QWinRTUiaTableProvider>(id());
407             return provider.CopyTo(returnValue);
408         }
409         break;
410     }
411     case PatternInterface_TableItem: {
412         // Item within a table/tree.
413         if (accessible->tableCellInterface()
414                 && ((accessible->role() == QAccessible::Cell) || (accessible->role() == QAccessible::TreeItem))) {
415             ComPtr<QWinRTUiaTableItemProvider> provider = Make<QWinRTUiaTableItemProvider>(id());
416             return provider.CopyTo(returnValue);
417         }
418         break;
419     }
420     case PatternInterface_Grid: {
421         // Table/tree.
422         if (accessible->tableInterface()
423                 && ((accessible->role() == QAccessible::Table) || (accessible->role() == QAccessible::Tree))) {
424             ComPtr<QWinRTUiaGridProvider> provider = Make<QWinRTUiaGridProvider>(id());
425             return provider.CopyTo(returnValue);
426         }
427         break;
428     }
429     case PatternInterface_GridItem: {
430         // Item within a table/tree.
431         if (accessible->tableCellInterface()
432                 && ((accessible->role() == QAccessible::Cell) || (accessible->role() == QAccessible::TreeItem))) {
433             ComPtr<QWinRTUiaGridItemProvider> provider = Make<QWinRTUiaGridItemProvider>(id());
434             return provider.CopyTo(returnValue);
435         }
436         break;
437     }
438     case PatternInterface_Invoke: {
439         // Things that have an invokable action (e.g., simple buttons).
440         if (accessible->actionInterface()) {
441             ComPtr<QWinRTUiaInvokeProvider> provider = Make<QWinRTUiaInvokeProvider>(id());
442             return provider.CopyTo(returnValue);
443         }
444         break;
445     }
446     default:
447         break;
448     }
449     return S_OK;
450 }
451 
GetAcceleratorKeyCore(HSTRING * returnValue)452 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::GetAcceleratorKeyCore(HSTRING *returnValue)
453 {
454     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
455 
456     if (!returnValue)
457         return E_INVALIDARG;
458     QSharedPointer<QWinRTUiaControlMetadata> metadata = QWinRTUiaMetadataCache::instance()->metadataForId(id());
459     return qHString(metadata->accelerator(), returnValue);
460 }
461 
GetAccessKeyCore(HSTRING * returnValue)462 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::GetAccessKeyCore(HSTRING *returnValue)
463 {
464     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
465 
466     if (!returnValue)
467         return E_INVALIDARG;
468     QSharedPointer<QWinRTUiaControlMetadata> metadata = QWinRTUiaMetadataCache::instance()->metadataForId(id());
469     return qHString(metadata->access(), returnValue);
470 }
471 
GetAutomationControlTypeCore(AutomationControlType * returnValue)472 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::GetAutomationControlTypeCore(AutomationControlType *returnValue)
473 {
474     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
475 
476     if (!returnValue)
477         return E_INVALIDARG;
478     QSharedPointer<QWinRTUiaControlMetadata> metadata = QWinRTUiaMetadataCache::instance()->metadataForId(id());
479     *returnValue = roleToControlType(metadata->role());
480     return S_OK;
481 }
482 
GetAutomationIdCore(HSTRING * returnValue)483 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::GetAutomationIdCore(HSTRING *returnValue)
484 {
485     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
486 
487     if (!returnValue)
488         return E_INVALIDARG;
489     QSharedPointer<QWinRTUiaControlMetadata> metadata = QWinRTUiaMetadataCache::instance()->metadataForId(id());
490     return qHString(metadata->automationId(), returnValue);
491 }
492 
493 // Returns the bounding rectangle for the accessible control.
GetBoundingRectangleCore(ABI::Windows::Foundation::Rect * returnValue)494 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::GetBoundingRectangleCore(ABI::Windows::Foundation::Rect *returnValue)
495 {
496     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
497 
498     if (!returnValue)
499         return E_INVALIDARG;
500 
501     QSharedPointer<QWinRTUiaControlMetadata> metadata = QWinRTUiaMetadataCache::instance()->metadataForId(id());
502 
503     QRect rect = metadata->boundingRect();
504     returnValue->X = rect.x();
505     returnValue->Y = rect.y();
506     returnValue->Width = rect.width();
507     returnValue->Height = rect.height();
508     return S_OK;
509 }
510 
GetChildrenCore(IVector<AutomationPeer * > ** returnValue)511 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::GetChildrenCore(IVector<AutomationPeer *> **returnValue)
512 {
513     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
514 
515     if (!returnValue)
516         return E_INVALIDARG;
517     *returnValue = nullptr;
518 
519     auto accid = id();
520     auto children = std::make_shared<QVarLengthArray<QAccessible::Id>>();
521 
522     if (!SUCCEEDED(QEventDispatcherWinRT::runOnMainThread([accid, children]() {
523         if (QAccessibleInterface *accessible = accessibleForId(accid)) {
524             int childCount = accessible->childCount();
525             for (int i = 0; i < childCount; ++i) {
526                 if (QAccessibleInterface *childAcc = accessible->child(i)) {
527                     QAccessible::Id childId = idForAccessible(childAcc);
528                     QWinRTUiaMetadataCache::instance()->load(childId);
529                     if (!childAcc->state().invisible)
530                         children->append(childId);
531                 }
532             }
533         }
534         return S_OK;
535     }))) {
536         return E_FAIL;
537     }
538 
539     ComPtr<IVector<AutomationPeer *>> peerVector = Make<QWinRTUiaPeerVector>();
540 
541     for (auto childId : *children) {
542         if (ComPtr<QWinRTUiaMainProvider> provider = providerForAccessibleId(childId)) {
543             IAutomationPeer *peer;
544             if (SUCCEEDED(provider.CopyTo(&peer)))
545                 peerVector->Append(peer);
546         }
547     }
548     return peerVector.CopyTo(returnValue);
549 }
550 
GetClassNameCore(HSTRING * returnValue)551 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::GetClassNameCore(HSTRING *returnValue)
552 {
553     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
554 
555     if (!returnValue)
556         return E_INVALIDARG;
557     QSharedPointer<QWinRTUiaControlMetadata> metadata = QWinRTUiaMetadataCache::instance()->metadataForId(id());
558     return qHString(metadata->className(), returnValue);
559 }
560 
GetClickablePointCore(ABI::Windows::Foundation::Point * returnValue)561 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::GetClickablePointCore(ABI::Windows::Foundation::Point *returnValue)
562 {
563     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
564 
565     if (!returnValue)
566         return E_INVALIDARG;
567     return E_NOTIMPL;
568 }
569 
GetHelpTextCore(HSTRING * returnValue)570 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::GetHelpTextCore(HSTRING *returnValue)
571 {
572     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
573 
574     if (!returnValue)
575         return E_INVALIDARG;
576     QSharedPointer<QWinRTUiaControlMetadata> metadata = QWinRTUiaMetadataCache::instance()->metadataForId(id());
577     return qHString(metadata->help(), returnValue);
578 }
579 
GetItemStatusCore(HSTRING * returnValue)580 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::GetItemStatusCore(HSTRING *returnValue)
581 {
582     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
583 
584     if (!returnValue)
585         return E_INVALIDARG;
586     return E_NOTIMPL;
587 }
588 
GetItemTypeCore(HSTRING * returnValue)589 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::GetItemTypeCore(HSTRING *returnValue)
590 {
591     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
592 
593     if (!returnValue)
594         return E_INVALIDARG;
595     return E_NOTIMPL;
596 }
597 
GetLabeledByCore(IAutomationPeer ** returnValue)598 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::GetLabeledByCore(IAutomationPeer **returnValue)
599 {
600     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
601 
602     if (!returnValue)
603         return E_INVALIDARG;
604     return E_NOTIMPL;
605 }
606 
GetLocalizedControlTypeCore(HSTRING * returnValue)607 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::GetLocalizedControlTypeCore(HSTRING *returnValue)
608 {
609     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
610 
611     if (!returnValue)
612         return E_INVALIDARG;
613     return E_NOTIMPL;
614 }
615 
GetNameCore(HSTRING * returnValue)616 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::GetNameCore(HSTRING *returnValue)
617 {
618     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
619 
620     if (!returnValue)
621         return E_INVALIDARG;
622     QSharedPointer<QWinRTUiaControlMetadata> metadata = QWinRTUiaMetadataCache::instance()->metadataForId(id());
623     return qHString(metadata->controlName(), returnValue);
624 }
625 
GetOrientationCore(AutomationOrientation * returnValue)626 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::GetOrientationCore(AutomationOrientation *returnValue)
627 {
628     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
629 
630     if (!returnValue)
631         return E_INVALIDARG;
632     *returnValue = AutomationOrientation_None;
633     return S_OK;
634 }
635 
HasKeyboardFocusCore(boolean * returnValue)636 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::HasKeyboardFocusCore(boolean *returnValue)
637 {
638     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
639 
640     if (!returnValue)
641         return E_INVALIDARG;
642     QSharedPointer<QWinRTUiaControlMetadata> metadata = QWinRTUiaMetadataCache::instance()->metadataForId(id());
643     *returnValue = (metadata->state().focused != 0);
644     return S_OK;
645 }
646 
IsContentElementCore(boolean * returnValue)647 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::IsContentElementCore(boolean *returnValue)
648 {
649     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
650 
651     if (!returnValue)
652         return E_INVALIDARG;
653     *returnValue = true;
654     return S_OK;
655 }
656 
IsControlElementCore(boolean * returnValue)657 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::IsControlElementCore(boolean *returnValue)
658 {
659     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
660 
661     if (!returnValue)
662         return E_INVALIDARG;
663     *returnValue = true;
664     return S_OK;
665 }
666 
IsEnabledCore(boolean * returnValue)667 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::IsEnabledCore(boolean *returnValue)
668 {
669     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
670 
671     if (!returnValue)
672         return E_INVALIDARG;
673     QSharedPointer<QWinRTUiaControlMetadata> metadata = QWinRTUiaMetadataCache::instance()->metadataForId(id());
674     *returnValue = (metadata->state().disabled == 0);
675     return S_OK;
676 }
677 
IsKeyboardFocusableCore(boolean * returnValue)678 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::IsKeyboardFocusableCore(boolean *returnValue)
679 {
680     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
681 
682     if (!returnValue)
683         return E_INVALIDARG;
684     QSharedPointer<QWinRTUiaControlMetadata> metadata = QWinRTUiaMetadataCache::instance()->metadataForId(id());
685     *returnValue = (metadata->state().focusable != 0);
686     return S_OK;
687 }
688 
IsOffscreenCore(boolean * returnValue)689 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::IsOffscreenCore(boolean *returnValue)
690 {
691     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
692 
693     if (!returnValue)
694         return E_INVALIDARG;
695     QSharedPointer<QWinRTUiaControlMetadata> metadata = QWinRTUiaMetadataCache::instance()->metadataForId(id());
696     *returnValue = (metadata->state().offscreen != 0);
697     return S_OK;
698 }
699 
IsPasswordCore(boolean * returnValue)700 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::IsPasswordCore(boolean *returnValue)
701 {
702     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
703 
704     if (!returnValue)
705         return E_INVALIDARG;
706     QSharedPointer<QWinRTUiaControlMetadata> metadata = QWinRTUiaMetadataCache::instance()->metadataForId(id());
707     *returnValue = (metadata->role() == QAccessible::EditableText) && (metadata->state().passwordEdit != 0);
708     return S_OK;
709 }
710 
IsRequiredForFormCore(boolean * returnValue)711 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::IsRequiredForFormCore(boolean *returnValue)
712 {
713     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
714 
715     if (!returnValue)
716         return E_INVALIDARG;
717     *returnValue = false;
718     return S_OK;
719 }
720 
721 // Sets focus to the control.
SetFocusCore()722 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::SetFocusCore()
723 {
724     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
725 
726     QAccessibleInterface *accessible = accessibleInterface();
727     if (!accessible)
728         return E_FAIL;
729 
730     QAccessibleActionInterface *actionInterface = accessible->actionInterface();
731     if (!actionInterface)
732         return E_FAIL;
733 
734     QEventDispatcherWinRT::runOnMainThread([actionInterface]() {
735         actionInterface->doAction(QAccessibleActionInterface::setFocusAction());
736         return S_OK;
737     });
738     return S_OK;
739 }
740 
741 // Returns a provider for the UI element present at the specified screen coordinates.
GetPeerFromPointCore(ABI::Windows::Foundation::Point point,IAutomationPeer ** returnValue)742 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::GetPeerFromPointCore(ABI::Windows::Foundation::Point point, IAutomationPeer **returnValue)
743 {
744     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
745 
746     if (!returnValue)
747         return E_INVALIDARG;
748     *returnValue = nullptr;
749 
750     // Scale coordinates from High DPI screens?
751 
752     auto accid = id();
753     auto elementId = std::make_shared<QAccessible::Id>(0);
754 
755     if (!SUCCEEDED(QEventDispatcherWinRT::runOnMainThread([accid, elementId, point]() {
756         // Controls can be embedded within grouping elements. By default returns the innermost control.
757         QAccessibleInterface *target = accessibleForId(accid);
758         while (QAccessibleInterface *tmpacc = target->childAt(point.X, point.Y)) {
759             target = tmpacc;
760             // For accessibility tools it may be better to return the text element instead of its subcomponents.
761             if (target->textInterface()) break;
762         }
763         *elementId = idForAccessible(target);
764         QWinRTUiaMetadataCache::instance()->load(*elementId);
765         return S_OK;
766     }))) {
767         return E_FAIL;
768     }
769 
770     if (ComPtr<QWinRTUiaMainProvider> provider = providerForAccessibleId(*elementId))
771         return provider.CopyTo(returnValue);
772     return E_FAIL;
773 }
774 
GetLiveSettingCore(AutomationLiveSetting * returnValue)775 HRESULT STDMETHODCALLTYPE QWinRTUiaMainProvider::GetLiveSettingCore(AutomationLiveSetting *returnValue)
776 {
777     qCDebug(lcQpaUiAutomation) << __FUNCTION__;
778 
779     if (!returnValue)
780         return E_INVALIDARG;
781     return E_NOTIMPL;
782 }
783 
784 QT_END_NAMESPACE
785 
786 #endif // QT_CONFIG(accessibility)
787 
788