Flutter Engine
The Flutter Engine
ax_platform_node_textprovider_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 <wrl/client.h>
8
10
12
13#define UIA_VALIDATE_TEXTPROVIDER_CALL() \
14 if (!owner()->GetDelegate()) \
15 return UIA_E_ELEMENTNOTAVAILABLE;
16#define UIA_VALIDATE_TEXTPROVIDER_CALL_1_ARG(arg) \
17 if (!owner()->GetDelegate()) \
18 return UIA_E_ELEMENTNOTAVAILABLE; \
19 if (!arg) \
20 return E_INVALIDARG;
21
22namespace ui {
23
24AXPlatformNodeTextProviderWin::AXPlatformNodeTextProviderWin() {}
25
26AXPlatformNodeTextProviderWin::~AXPlatformNodeTextProviderWin() {}
27
28// static
29AXPlatformNodeTextProviderWin* AXPlatformNodeTextProviderWin::Create(
30 AXPlatformNodeWin* owner) {
31 CComObject<AXPlatformNodeTextProviderWin>* text_provider = nullptr;
32 if (SUCCEEDED(CComObject<AXPlatformNodeTextProviderWin>::CreateInstance(
33 &text_provider))) {
34 BASE_DCHECK(text_provider);
35 text_provider->owner_ = owner;
36 text_provider->AddRef();
37 return text_provider;
38 }
39
40 return nullptr;
41}
42
43// static
44void AXPlatformNodeTextProviderWin::CreateIUnknown(AXPlatformNodeWin* owner,
45 IUnknown** unknown) {
46 Microsoft::WRL::ComPtr<AXPlatformNodeTextProviderWin> text_provider(
47 Create(owner));
48 if (text_provider)
49 *unknown = text_provider.Detach();
50}
51
52//
53// ITextProvider methods.
54//
55
56HRESULT AXPlatformNodeTextProviderWin::GetSelection(SAFEARRAY** selection) {
58
59 *selection = nullptr;
60
61 AXPlatformNodeDelegate* delegate = owner()->GetDelegate();
62 AXTree::Selection unignored_selection = delegate->GetUnignoredSelection();
63
64 AXPlatformNode* anchor_object =
65 delegate->GetFromNodeID(unignored_selection.anchor_object_id);
66 AXPlatformNode* focus_object =
67 delegate->GetFromNodeID(unignored_selection.focus_object_id);
68
69 // anchor_offset corresponds to the selection start index
70 // and focus_offset is where the selection ends.
71 auto start_offset = unignored_selection.anchor_offset;
72 auto end_offset = unignored_selection.focus_offset;
73
74 // If there's no selected object, return success and don't fill the SAFEARRAY.
75 if (!anchor_object || !focus_object)
76 return S_OK;
77
79 anchor_object->GetDelegate()->CreateTextPositionAt(start_offset);
81 focus_object->GetDelegate()->CreateTextPositionAt(end_offset);
82
83 BASE_DCHECK(!start->IsNullPosition());
84 BASE_DCHECK(!end->IsNullPosition());
85
86 // Reverse start and end if the selection goes backwards
87 if (*start > *end)
89
90 Microsoft::WRL::ComPtr<ITextRangeProvider> text_range_provider =
91 AXPlatformNodeTextRangeProviderWin::CreateTextRangeProvider(
92 std::move(start), std::move(end));
93 if (&text_range_provider == nullptr)
94 return E_OUTOFMEMORY;
95
96 // Since we don't support disjoint text ranges, the SAFEARRAY returned
97 // will always have one element
98 base::win::ScopedSafearray selections_to_return(
99 SafeArrayCreateVector(VT_UNKNOWN /* element type */, 0 /* lower bound */,
100 1 /* number of elements */));
101
102 if (!selections_to_return.Get())
103 return E_OUTOFMEMORY;
104
105 LONG index = 0;
106 HRESULT hr = SafeArrayPutElement(selections_to_return.Get(), &index,
107 text_range_provider.Get());
109
110 // Since BASE_DCHECK only happens in debug builds, return immediately to
111 // ensure that we're not leaking the SAFEARRAY on release builds
112 if (FAILED(hr))
113 return E_FAIL;
114
115 *selection = selections_to_return.Release();
116
117 return S_OK;
118}
119
120HRESULT AXPlatformNodeTextProviderWin::GetVisibleRanges(
121 SAFEARRAY** visible_ranges) {
123
124 const AXPlatformNodeDelegate* delegate = owner()->GetDelegate();
125
126 // Get the Clipped Frame Bounds of the current node, not from the root,
127 // so if this node is wrapped with overflow styles it will have the
128 // correct bounds
129 const gfx::Rect frame_rect = delegate->GetBoundsRect(
131
132 const auto start = delegate->CreateTextPositionAt(0);
133 const auto end = start->CreatePositionAtEndOfAnchor();
134 BASE_DCHECK(start->GetAnchor() == end->GetAnchor());
135
136 // SAFEARRAYs are not dynamic, so fill the visible ranges in a vector
137 // and then transfer to an appropriately-sized SAFEARRAY
138 std::vector<Microsoft::WRL::ComPtr<ITextRangeProvider>> ranges;
139
140 auto current_line_start = start->Clone();
141 while (!current_line_start->IsNullPosition() && *current_line_start < *end) {
142 auto current_line_end = current_line_start->CreateNextLineEndPosition(
144 if (current_line_end->IsNullPosition() || *current_line_end > *end)
145 current_line_end = end->Clone();
146
147 gfx::Rect current_rect = delegate->GetInnerTextRangeBoundsRect(
148 current_line_start->text_offset(), current_line_end->text_offset(),
150
151 if (frame_rect.Contains(current_rect)) {
152 Microsoft::WRL::ComPtr<ITextRangeProvider> text_range_provider =
153 AXPlatformNodeTextRangeProviderWin::CreateTextRangeProvider(
154 current_line_start->Clone(), current_line_end->Clone());
155
156 ranges.emplace_back(text_range_provider);
157 }
158
159 current_line_start = current_line_start->CreateNextLineStartPosition(
161 }
162
163 base::win::ScopedSafearray scoped_visible_ranges(
164 SafeArrayCreateVector(VT_UNKNOWN /* element type */, 0 /* lower bound */,
165 ranges.size() /* number of elements */));
166
167 if (!scoped_visible_ranges.Get())
168 return E_OUTOFMEMORY;
169
170 LONG index = 0;
171 for (Microsoft::WRL::ComPtr<ITextRangeProvider>& current_provider : ranges) {
172 HRESULT hr = SafeArrayPutElement(scoped_visible_ranges.Get(), &index,
173 current_provider.Get());
175
176 // Since BASE_DCHECK only happens in debug builds, return immediately to
177 // ensure that we're not leaking the SAFEARRAY on release builds
178 if (FAILED(hr))
179 return E_FAIL;
180
181 ++index;
182 }
183
184 *visible_ranges = scoped_visible_ranges.Release();
185
186 return S_OK;
187}
188
189HRESULT AXPlatformNodeTextProviderWin::RangeFromChild(
190 IRawElementProviderSimple* child,
191 ITextRangeProvider** range) {
193
194 *range = nullptr;
195
196 Microsoft::WRL::ComPtr<ui::AXPlatformNodeWin> child_platform_node;
197 if (!SUCCEEDED(child->QueryInterface(IID_PPV_ARGS(&child_platform_node))))
198 return UIA_E_INVALIDOPERATION;
199
200 if (!owner()->IsDescendant(child_platform_node.Get()))
201 return E_INVALIDARG;
202
203 *range = GetRangeFromChild(owner(), child_platform_node.Get());
204
205 return S_OK;
206}
207
208HRESULT AXPlatformNodeTextProviderWin::RangeFromPoint(
209 UiaPoint uia_point,
210 ITextRangeProvider** range) {
212 *range = nullptr;
213
214 gfx::Point point(uia_point.x, uia_point.y);
215 // Retrieve the closest accessibility node. No coordinate unit conversion is
216 // needed, hit testing input is also in screen coordinates.
217
218 AXPlatformNodeWin* nearest_node =
219 static_cast<AXPlatformNodeWin*>(owner()->NearestLeafToPoint(point));
220 BASE_DCHECK(nearest_node);
221 BASE_DCHECK(nearest_node->IsLeaf());
222
224 start = nearest_node->GetDelegate()->CreateTextPositionAt(
225 nearest_node->NearestTextIndexToPoint(point));
226 BASE_DCHECK(!start->IsNullPosition());
227 end = start->Clone();
228
229 *range = AXPlatformNodeTextRangeProviderWin::CreateTextRangeProvider(
230 std::move(start), std::move(end));
231 return S_OK;
232}
233
234HRESULT AXPlatformNodeTextProviderWin::get_DocumentRange(
235 ITextRangeProvider** range) {
237
238 // Get range from child, where child is the current node. In other words,
239 // getting the text range of the current owner AxPlatformNodeWin node.
240 *range = GetRangeFromChild(owner(), owner());
241
242 return S_OK;
243}
244
245HRESULT AXPlatformNodeTextProviderWin::get_SupportedTextSelection(
246 enum SupportedTextSelection* text_selection) {
248
249 *text_selection = SupportedTextSelection_Single;
250 return S_OK;
251}
252
253//
254// ITextEditProvider methods.
255//
256
257HRESULT AXPlatformNodeTextProviderWin::GetActiveComposition(
258 ITextRangeProvider** range) {
260
261 *range = nullptr;
262 return GetTextRangeProviderFromActiveComposition(range);
263}
264
265HRESULT AXPlatformNodeTextProviderWin::GetConversionTarget(
266 ITextRangeProvider** range) {
268
269 *range = nullptr;
270 return GetTextRangeProviderFromActiveComposition(range);
271}
272
273ITextRangeProvider* AXPlatformNodeTextProviderWin::GetRangeFromChild(
274 ui::AXPlatformNodeWin* ancestor,
275 ui::AXPlatformNodeWin* descendant) {
276 BASE_DCHECK(ancestor);
277 BASE_DCHECK(descendant);
278 BASE_DCHECK(descendant->GetDelegate());
279 BASE_DCHECK(ancestor->IsDescendant(descendant));
280
281 // Start and end should be leaf text positions that span the beginning and end
282 // of text content within a node. The start position should be the directly
283 // first child and the end position should be the deepest last child node.
285 descendant->GetDelegate()->CreateTextPositionAt(0)->AsLeafTextPosition();
286
288 if (descendant->GetChildCount() == 0) {
289 end = descendant->GetDelegate()
290 ->CreateTextPositionAt(0)
291 ->CreatePositionAtEndOfAnchor()
292 ->AsLeafTextPosition();
293 } else {
294 AXPlatformNodeBase* deepest_last_child = descendant->GetLastChild();
295 while (deepest_last_child && deepest_last_child->GetChildCount() > 0)
296 deepest_last_child = deepest_last_child->GetLastChild();
297
298 end = deepest_last_child->GetDelegate()
299 ->CreateTextPositionAt(0)
300 ->CreatePositionAtEndOfAnchor()
301 ->AsLeafTextPosition();
302 }
303
304 return AXPlatformNodeTextRangeProviderWin::CreateTextRangeProvider(
305 std::move(start), std::move(end));
306}
307
308ITextRangeProvider* AXPlatformNodeTextProviderWin::CreateDegenerateRangeAtStart(
309 ui::AXPlatformNodeWin* node) {
310 BASE_DCHECK(node);
311 BASE_DCHECK(node->GetDelegate());
312
313 // Create a degenerate range positioned at the node's start.
315 start = node->GetDelegate()->CreateTextPositionAt(0)->AsLeafTextPosition();
316 end = start->Clone();
317 return AXPlatformNodeTextRangeProviderWin::CreateTextRangeProvider(
318 std::move(start), std::move(end));
319}
320
321ui::AXPlatformNodeWin* AXPlatformNodeTextProviderWin::owner() const {
322 return owner_.Get();
323}
324
325HRESULT
326AXPlatformNodeTextProviderWin::GetTextRangeProviderFromActiveComposition(
327 ITextRangeProvider** range) {
328 *range = nullptr;
329 // We fetch the start and end offset of an active composition only if
330 // this object has focus and TSF is in composition mode.
331 // The offsets here refer to the character positions in a plain text
332 // view of the DOM tree. Ex: if the active composition in an element
333 // has "abc" then the range will be (0,3) in both TSF and accessibility
335 owner()->GetDelegate()->GetFocus()) ==
336 static_cast<AXPlatformNode*>(owner())) &&
337 owner()->HasActiveComposition()) {
338 gfx::Range active_composition_offset =
339 owner()->GetActiveCompositionOffsets();
341 owner()->GetDelegate()->CreateTextPositionAt(
342 /*offset*/ active_composition_offset.start());
344 owner()->GetDelegate()->CreateTextPositionAt(
345 /*offset*/ active_composition_offset.end());
346
347 *range = AXPlatformNodeTextRangeProviderWin::CreateTextRangeProvider(
348 std::move(start), std::move(end));
349 }
350
351 return S_OK;
352}
353
354} // namespace ui
static sk_sp< Effect > Create()
Definition: RefCntTest.cpp:117
void swap(sk_sp< T > &a, sk_sp< T > &b)
Definition: SkRefCnt.h:341
#define UIA_VALIDATE_TEXTPROVIDER_CALL()
#define UIA_VALIDATE_TEXTPROVIDER_CALL_1_ARG(arg)
constexpr uint32_t end() const
Definition: range.h:59
constexpr uint32_t start() const
Definition: range.h:56
Definition: rect.h:36
bool Contains(int point_x, int point_y) const
Definition: rect.cc:156
static AXPlatformNode * FromNativeViewAccessible(gfx::NativeViewAccessible accessible)
std::unique_ptr< AXPosition< AXNodePosition, AXNode > > AXPositionInstance
Definition: ax_position.h:163
glong glong end
#define BASE_DCHECK(condition)
Definition: logging.h:63
#define SUCCEEDED(hr)
long LONG
Definition: windows_types.h:23
#define FAILED(hr)