Flutter Engine
The Flutter Engine
ax_fragment_root_win.cc
Go to the documentation of this file.
1// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include <unordered_map>
8
11#include "base/win/atl_module.h"
12#include "uia_registrar_win.h"
13
14namespace ui {
15
16class AXFragmentRootPlatformNodeWin : public AXPlatformNodeWin,
17 public IItemContainerProvider,
18 public IRawElementProviderFragmentRoot,
19 public IRawElementProviderAdviseEvents {
20 public:
22 COM_INTERFACE_ENTRY(IItemContainerProvider)
23 COM_INTERFACE_ENTRY(IRawElementProviderFragmentRoot)
24 COM_INTERFACE_ENTRY(IRawElementProviderAdviseEvents)
25 COM_INTERFACE_ENTRY_CHAIN(AXPlatformNodeWin)
26 END_COM_MAP()
27
29 AXPlatformNodeDelegate* delegate) {
30 // Make sure ATL is initialized in this module.
32
33 CComObject<AXFragmentRootPlatformNodeWin>* instance = nullptr;
34 HRESULT hr =
35 CComObject<AXFragmentRootPlatformNodeWin>::CreateInstance(&instance);
37 instance->Init(delegate);
38 instance->AddRef();
39 return instance;
40 }
41
42 //
43 // IItemContainerProvider methods.
44 //
45 IFACEMETHODIMP FindItemByProperty(
46 IRawElementProviderSimple* start_after_element,
47 PROPERTYID property_id,
49 IRawElementProviderSimple** result) override {
51 *result = nullptr;
52
53 // We currently only support the custom UIA property ID for unique id.
54 if (property_id ==
55 UiaRegistrarWin::GetInstance().GetUiaUniqueIdPropertyId() &&
56 value.vt == VT_BSTR) {
57 int32_t ax_unique_id;
58
59 // TODO(gw280): https://github.com/flutter/flutter/issues/78802
60 // detect and handle errors
61 ax_unique_id = std::stoi(value.bstrVal);
62
63 // In the Windows accessibility platform implementation, id 0 represents
64 // self; a positive id represents the immediate descendants; and a
65 // negative id represents a unique id that can be mapped to any node.
66 if (AXPlatformNodeWin* result_platform_node =
67 static_cast<AXPlatformNodeWin*>(GetFromUniqueId(-ax_unique_id))) {
68 if (start_after_element) {
69 Microsoft::WRL::ComPtr<AXPlatformNodeWin> start_after_platform_node;
70 if (!SUCCEEDED(start_after_element->QueryInterface(
71 IID_PPV_ARGS(&start_after_platform_node))))
72 return E_INVALIDARG;
73
74 // We want |result| to be nullptr if it comes before or is equal to
75 // |start_after_element|.
76 if (start_after_platform_node->CompareTo(*result_platform_node) >= 0)
77 return S_OK;
78 }
79
80 return result_platform_node->QueryInterface(IID_PPV_ARGS(result));
81 }
82 }
83
84 return E_INVALIDARG;
85 }
86
87 //
88 // IRawElementProviderSimple methods.
89 //
90
92 IRawElementProviderSimple** host_element_provider) override {
93 UIA_VALIDATE_CALL_1_ARG(host_element_provider);
94
95 HWND hwnd = GetDelegate()->GetTargetForNativeAccessibilityEvent();
96 return UiaHostProviderFromHwnd(hwnd, host_element_provider);
97 }
98
99 IFACEMETHODIMP GetPatternProvider(PATTERNID pattern_id,
100 IUnknown** result) override {
102 *result = nullptr;
103
104 if (pattern_id == UIA_ItemContainerPatternId) {
105 AddRef();
106 *result = static_cast<IItemContainerProvider*>(this);
107 return S_OK;
108 }
109
110 return AXPlatformNodeWin::GetPatternProviderImpl(pattern_id, result);
111 }
112
113 IFACEMETHODIMP GetPropertyValue(PROPERTYID property_id,
114 VARIANT* result) override {
116
117 switch (property_id) {
118 default:
119 // UIA has a built-in provider that will expose values for several
120 // properties based on the HWND. This information is useful to someone
121 // examining the accessibility tree using tools such as Inspect. Return
122 // VT_EMPTY for most properties so that we don't override values from
123 // the default provider with blank data.
124 result->vt = VT_EMPTY;
125 break;
126
127 case UIA_IsControlElementPropertyId:
128 case UIA_IsContentElementPropertyId:
129 // Override IsControlElement and IsContentElement to fine tune which
130 // fragment roots appear in the control and content views.
131 result->vt = VT_BOOL;
132 result->boolVal =
133 static_cast<AXFragmentRootWin*>(GetDelegate())->IsControlElement()
134 ? VARIANT_TRUE
135 : VARIANT_FALSE;
136 break;
137 }
138
139 return S_OK;
140 }
141
142 //
143 // IRawElementProviderFragment methods.
144 //
145
146 IFACEMETHODIMP get_FragmentRoot(
147 IRawElementProviderFragmentRoot** fragment_root) override {
148 UIA_VALIDATE_CALL_1_ARG(fragment_root);
149
150 QueryInterface(IID_PPV_ARGS(fragment_root));
151 return S_OK;
152 }
153
154 //
155 // IRawElementProviderFragmentRoot methods.
156 //
158 double screen_physical_pixel_x,
159 double screen_physical_pixel_y,
160 IRawElementProviderFragment** element_provider) override {
161 UIA_VALIDATE_CALL_1_ARG(element_provider);
162
163 *element_provider = nullptr;
164
165 gfx::NativeViewAccessible hit_element = nullptr;
166
167 // Descend the tree until we get a non-hit or can't go any further.
168 AXPlatformNode* node_to_test = this;
169 do {
170 gfx::NativeViewAccessible test_result =
171 node_to_test->GetDelegate()->HitTestSync(screen_physical_pixel_x,
172 screen_physical_pixel_y);
173 if (test_result != nullptr && test_result != hit_element) {
174 hit_element = test_result;
175 node_to_test = AXPlatformNode::FromNativeViewAccessible(test_result);
176 } else {
177 node_to_test = nullptr;
178 }
179 } while (node_to_test);
180
181 if (hit_element)
182 hit_element->QueryInterface(element_provider);
183
184 return S_OK;
185 }
186
187 IFACEMETHODIMP GetFocus(IRawElementProviderFragment** focus) override {
189
190 *focus = nullptr;
191
192 gfx::NativeViewAccessible focused_element = nullptr;
193
194 // GetFocus() can return a node at the root of a subtree, for example when
195 // transitioning from Views into web content. In such cases we want to
196 // continue drilling to retrieve the actual focused element.
197 AXPlatformNode* node_to_test = this;
198 do {
199 gfx::NativeViewAccessible test_result =
200 node_to_test->GetDelegate()->GetFocus();
201 if (test_result != nullptr && test_result != focused_element) {
202 focused_element = test_result;
203 node_to_test =
205 } else {
206 node_to_test = nullptr;
207 }
208 } while (node_to_test);
209
210 if (focused_element)
211 focused_element->QueryInterface(IID_PPV_ARGS(focus));
212
213 return S_OK;
214 }
215
216 //
217 // IRawElementProviderAdviseEvents methods.
218 //
219 IFACEMETHODIMP AdviseEventAdded(EVENTID event_id,
220 SAFEARRAY* property_ids) override {
221 if (event_id == UIA_LiveRegionChangedEventId) {
222 live_region_change_listeners_++;
223
224 if (live_region_change_listeners_ == 1) {
225 // Fire a LiveRegionChangedEvent for each live-region to tell the
226 // newly-attached assistive technology about the regions.
227 //
228 // Ideally we'd be able to direct these events to only the
229 // newly-attached AT, but we don't have that capability, so we only
230 // fire events when the *first* AT attaches. (A common scenario will
231 // be an attached screen-reader, then a software-keyboard attaches to
232 // handle an input field; we don't want the screen-reader to announce
233 // that every live-region has changed.) There isn't a perfect solution,
234 // but this heuristic seems to work well in practice.
235 FireLiveRegionChangeRecursive();
236 }
237 }
238 return S_OK;
239 }
240
241 IFACEMETHODIMP AdviseEventRemoved(EVENTID event_id,
242 SAFEARRAY* property_ids) override {
243 if (event_id == UIA_LiveRegionChangedEventId) {
244 BASE_DCHECK(live_region_change_listeners_ > 0);
245 live_region_change_listeners_--;
246 }
247 return S_OK;
248 }
249
250 private:
251 int32_t live_region_change_listeners_ = 0;
252};
253
255 public:
258 return *instance;
259 }
260
261 void AddFragmentRoot(gfx::AcceleratedWidget widget,
262 AXFragmentRootWin* fragment_root) {
263 map_[widget] = fragment_root;
264 }
265
266 void RemoveFragmentRoot(gfx::AcceleratedWidget widget) { map_.erase(widget); }
267
268 ui::AXFragmentRootWin* GetFragmentRoot(gfx::AcceleratedWidget widget) const {
269 const auto& entry = map_.find(widget);
270 if (entry != map_.end())
271 return entry->second;
272
273 return nullptr;
274 }
275
277 gfx::NativeViewAccessible accessible) const {
278 for (const auto& entry : map_) {
279 AXPlatformNodeDelegate* child = entry.second->GetChildNodeDelegate();
280 if (child && (child->GetNativeViewAccessible() == accessible))
281 return entry.second;
282 }
283 return nullptr;
284 }
285
286 private:
287 std::unordered_map<gfx::AcceleratedWidget, AXFragmentRootWin*> map_;
288};
289
290AXFragmentRootWin::AXFragmentRootWin(gfx::AcceleratedWidget widget,
292 : widget_(widget), delegate_(delegate), alert_node_(nullptr) {
293 platform_node_ = ui::AXFragmentRootPlatformNodeWin::Create(this);
295}
296
299 platform_node_->Destroy();
300 platform_node_ = nullptr;
301}
302
304 gfx::AcceleratedWidget widget) {
306}
307
308// static
310 gfx::NativeViewAccessible accessible) {
312 accessible);
313}
314
316 return platform_node_.Get();
317}
318
320 return delegate_->IsAXFragmentRootAControlElement();
321}
322
323gfx::NativeViewAccessible AXFragmentRootWin::GetParent() {
324 return delegate_->GetParentOfAXFragmentRoot();
325}
326
327int AXFragmentRootWin::GetChildCount() const {
328 return delegate_->GetChildOfAXFragmentRoot() ? 1 : 0;
329}
330
331gfx::NativeViewAccessible AXFragmentRootWin::ChildAtIndex(int index) {
332 if (index == 0) {
333 return delegate_->GetChildOfAXFragmentRoot();
334 } else if (index == 1 && alert_node_) {
335 return alert_node_;
336 }
337
338 return nullptr;
339}
340
341gfx::NativeViewAccessible AXFragmentRootWin::GetNextSibling() {
342 int child_index = GetIndexInParentOfChild();
343 if (child_index >= 0) {
344 AXPlatformNodeDelegate* parent = GetParentNodeDelegate();
345 if (parent && child_index < (parent->GetChildCount() - 1))
346 return GetParentNodeDelegate()->ChildAtIndex(child_index + 1);
347 }
348
349 return nullptr;
350}
351
352gfx::NativeViewAccessible AXFragmentRootWin::GetPreviousSibling() {
353 int child_index = GetIndexInParentOfChild();
354 if (child_index > 0)
355 return GetParentNodeDelegate()->ChildAtIndex(child_index - 1);
356
357 return nullptr;
358}
359
360gfx::NativeViewAccessible AXFragmentRootWin::HitTestSync(int x, int y) const {
362 if (child_delegate)
363 return child_delegate->HitTestSync(x, y);
364
365 return nullptr;
366}
367
368gfx::NativeViewAccessible AXFragmentRootWin::GetFocus() {
370 if (child_delegate)
371 return child_delegate->GetFocus();
372
373 return nullptr;
374}
375
376const ui::AXUniqueId& AXFragmentRootWin::GetUniqueId() const {
377 return unique_id_;
378}
379
380gfx::AcceleratedWidget
382 return widget_;
383}
384
385AXPlatformNode* AXFragmentRootWin::GetFromTreeIDAndNodeID(
386 const ui::AXTreeID& ax_tree_id,
387 int32_t node_id) {
389 if (child_delegate)
390 return child_delegate->GetFromTreeIDAndNodeID(ax_tree_id, node_id);
391
392 return nullptr;
393}
394
395AXPlatformNodeDelegate* AXFragmentRootWin::GetParentNodeDelegate() const {
397 if (parent)
399
400 return nullptr;
401}
402
405 if (child)
407
408 return nullptr;
409}
410
411int AXFragmentRootWin::GetIndexInParentOfChild() const {
412 AXPlatformNodeDelegate* parent = GetParentNodeDelegate();
413
414 if (!parent)
415 return 0;
416
418 if (child) {
419 int child_count = parent->GetChildCount();
420 for (int child_index = 0; child_index < child_count; child_index++) {
422 parent->ChildAtIndex(child_index))
423 ->GetDelegate() == child)
424 return child_index;
425 }
426 }
427 return 0;
428}
429
430void AXFragmentRootWin::SetAlertNode(AXPlatformNodeWin* alert_node) {
431 alert_node_ = alert_node;
432}
433
434gfx::Rect AXFragmentRootWin::GetBoundsRect(AXCoordinateSystem sys,
436 AXOffscreenResult* result) const {
438 if (!child) {
439 return gfx::Rect();
440 }
441 return child->GetBoundsRect(sys, clip, result);
442}
443
444} // namespace ui
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
#define UIA_VALIDATE_CALL_1_ARG(arg)
Definition: rect.h:36
virtual gfx::NativeViewAccessible GetChildOfAXFragmentRoot()=0
virtual gfx::NativeViewAccessible GetParentOfAXFragmentRoot()=0
virtual bool IsAXFragmentRootAControlElement()=0
static AXFragmentRootMapWin & GetInstance()
void RemoveFragmentRoot(gfx::AcceleratedWidget widget)
void AddFragmentRoot(gfx::AcceleratedWidget widget, AXFragmentRootWin *fragment_root)
ui::AXFragmentRootWin * GetFragmentRootParentOf(gfx::NativeViewAccessible accessible) const
ui::AXFragmentRootWin * GetFragmentRoot(gfx::AcceleratedWidget widget) const
IFACEMETHODIMP AdviseEventAdded(EVENTID event_id, SAFEARRAY *property_ids) override
IFACEMETHODIMP GetPatternProvider(PATTERNID pattern_id, IUnknown **result) override
IFACEMETHODIMP AdviseEventRemoved(EVENTID event_id, SAFEARRAY *property_ids) override
IFACEMETHODIMP FindItemByProperty(IRawElementProviderSimple *start_after_element, PROPERTYID property_id, VARIANT value, IRawElementProviderSimple **result) override
static AXFragmentRootPlatformNodeWin * Create(AXPlatformNodeDelegate *delegate)
IFACEMETHODIMP GetPropertyValue(PROPERTYID property_id, VARIANT *result) override
IFACEMETHODIMP get_FragmentRoot(IRawElementProviderFragmentRoot **fragment_root) override
IFACEMETHODIMP ElementProviderFromPoint(double screen_physical_pixel_x, double screen_physical_pixel_y, IRawElementProviderFragment **element_provider) override
IFACEMETHODIMP get_HostRawElementProvider(IRawElementProviderSimple **host_element_provider) override
IFACEMETHODIMP GetFocus(IRawElementProviderFragment **focus) override
gfx::NativeViewAccessible GetNativeViewAccessible() override
static AXFragmentRootWin * GetForAcceleratedWidget(gfx::AcceleratedWidget widget)
void SetAlertNode(AXPlatformNodeWin *alert_node)
static AXFragmentRootWin * GetFragmentRootParentOf(gfx::NativeViewAccessible accessible)
gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override
AXFragmentRootWin(gfx::AcceleratedWidget widget, AXFragmentRootDelegateWin *delegate)
AXPlatformNodeDelegate * GetChildNodeDelegate() const
virtual gfx::NativeViewAccessible GetFocus()=0
virtual gfx::NativeViewAccessible HitTestSync(int screen_physical_pixel_x, int screen_physical_pixel_y) const =0
virtual gfx::NativeViewAccessible GetNativeViewAccessible()=0
virtual gfx::Rect GetBoundsRect(const AXCoordinateSystem coordinate_system, const AXClippingBehavior clipping_behavior, AXOffscreenResult *offscreen_result=nullptr) const =0
virtual AXPlatformNode * GetFromTreeIDAndNodeID(const ui::AXTreeID &ax_tree_id, int32_t id)=0
virtual gfx::NativeViewAccessible ChildAtIndex(int index)=0
virtual int GetChildCount() const =0
static AXPlatformNode * FromNativeViewAccessible(gfx::NativeViewAccessible accessible)
virtual AXPlatformNodeDelegate * GetDelegate() const =0
static const UiaRegistrarWin & GetInstance()
MockDelegate delegate_
VkInstance instance
Definition: main.cc:48
uint8_t value
GAsyncResult * result
double y
double x
bool stoi(std::string_view s, SKSL_INT *value)
Definition: SkSLString.cpp:66
UnimplementedNativeViewAccessible * NativeViewAccessible
TRect< Scalar > Rect
Definition: rect.h:769
void CreateATLModuleIfNeeded()
Definition: atl_module.h:22
#define BASE_DCHECK(condition)
Definition: logging.h:63
#define SUCCEEDED(hr)