Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Classes | Namespaces | Macros | Functions
ax_platform_node_textprovider_win_unittest.cc File Reference
#include "ax/platform/ax_platform_node_win_unittest.h"
#include <UIAutomationClient.h>
#include <UIAutomationCoreApi.h>
#include <vector>
#include "ax/ax_action_data.h"
#include "ax/platform/ax_fragment_root_win.h"
#include "ax/platform/ax_platform_node_textprovider_win.h"
#include "ax/platform/ax_platform_node_textrangeprovider_win.h"
#include "ax/platform/test_ax_node_wrapper.h"
#include "base/win/scoped_bstr.h"
#include "base/win/scoped_safearray.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/platform/win/wstring_conversion.h"

Go to the source code of this file.

Classes

class  ui::AXPlatformNodeTextProviderTest
 

Namespaces

namespace  ui
 

Macros

#define EXPECT_UIA_INVALIDOPERATION(expr)    EXPECT_EQ(static_cast<HRESULT>(UIA_E_INVALIDOPERATION), (expr))
 
#define EXPECT_INVALIDARG(expr)    EXPECT_EQ(static_cast<HRESULT>(E_INVALIDARG), (expr))
 

Functions

 ui::TEST_F (AXPlatformNodeTextProviderTest, CreateDegenerateRangeFromStart)
 
 ui::TEST_F (AXPlatformNodeTextProviderTest, ITextProviderRangeFromChild)
 
 ui::TEST_F (AXPlatformNodeTextProviderTest, ITextProviderRangeFromChildMultipleChildren)
 
 ui::TEST_F (AXPlatformNodeTextProviderTest, NearestTextIndexToPoint)
 
 ui::TEST_F (AXPlatformNodeTextProviderTest, ITextProviderDocumentRange)
 
 ui::TEST_F (AXPlatformNodeTextProviderTest, DISABLED_ITextProviderDocumentRangeTrailingIgnored)
 
 ui::TEST_F (AXPlatformNodeTextProviderTest, ITextProviderDocumentRangeNested)
 
 ui::TEST_F (AXPlatformNodeTextProviderTest, ITextProviderSupportedSelection)
 
 ui::TEST_F (AXPlatformNodeTextProviderTest, ITextProviderGetSelection)
 
 ui::TEST_F (AXPlatformNodeTextProviderTest, ITextProviderGetActiveComposition)
 
 ui::TEST_F (AXPlatformNodeTextProviderTest, ITextProviderGetConversionTarget)
 

Macro Definition Documentation

◆ EXPECT_INVALIDARG

#define EXPECT_INVALIDARG (   expr)     EXPECT_EQ(static_cast<HRESULT>(E_INVALIDARG), (expr))

Definition at line 30 of file ax_platform_node_textprovider_win_unittest.cc.

32 : public AXPlatformNodeWinTest {
33 public:
34 AXPlatformNodeTextProviderTest() = default;
35 ~AXPlatformNodeTextProviderTest() override = default;
36 AXPlatformNodeTextProviderTest(const AXPlatformNodeTextProviderTest&) =
37 delete;
38 AXPlatformNodeTextProviderTest& operator=(
39 const AXPlatformNodeTextProviderTest&) = delete;
40
41 protected:
42 void SetOwner(AXPlatformNodeWin* owner,
43 ITextRangeProvider* destination_range) {
44 ComPtr<ITextRangeProvider> destination_provider = destination_range;
45 ComPtr<AXPlatformNodeTextRangeProviderWin> destination_provider_interal;
46
47 destination_provider->QueryInterface(
48 IID_PPV_ARGS(&destination_provider_interal));
49 destination_provider_interal->SetOwnerForTesting(owner);
50 }
51 AXPlatformNodeWin* GetOwner(
52 const AXPlatformNodeTextProviderWin* text_provider) {
53 return text_provider->owner_.Get();
54 }
55 const AXNodePosition::AXPositionInstance& GetStart(
56 const AXPlatformNodeTextRangeProviderWin* text_range) {
57 return text_range->start();
58 }
59 const AXNodePosition::AXPositionInstance& GetEnd(
60 const AXPlatformNodeTextRangeProviderWin* text_range) {
61 return text_range->end();
62 }
63};
64
65TEST_F(AXPlatformNodeTextProviderTest, CreateDegenerateRangeFromStart) {
66 AXNodeData text1_data;
67 text1_data.id = 3;
68 text1_data.role = ax::mojom::Role::kStaticText;
69 text1_data.SetName("some text");
70
71 AXNodeData text2_data;
72 text2_data.id = 4;
73 text2_data.role = ax::mojom::Role::kStaticText;
74 text2_data.SetName("more text");
75
76 AXNodeData link_data;
77 link_data.id = 2;
78 link_data.role = ax::mojom::Role::kLink;
79 link_data.child_ids = {3, 4};
80
81 AXNodeData root_data;
82 root_data.id = 1;
83 root_data.role = ax::mojom::Role::kStaticText;
84 root_data.SetName("Document");
85 root_data.child_ids = {2};
86
88 AXTreeData tree_data;
89 tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
90 update.tree_data = tree_data;
91 update.has_tree_data = true;
92 update.root_id = root_data.id;
93 update.nodes = {root_data, link_data, text1_data, text2_data};
94
95 Init(update);
96 AXNode* root_node = GetRootAsAXNode();
97 AXNode* link_node = root_node->children()[0];
98 AXNode* text2_node = link_node->children()[1];
99 AXPlatformNodeWin* owner =
100 static_cast<AXPlatformNodeWin*>(AXPlatformNodeFromNode(root_node));
101 BASE_DCHECK(owner);
102
103 ComPtr<IRawElementProviderSimple> root_node_raw =
104 QueryInterfaceFromNode<IRawElementProviderSimple>(root_node);
105 ComPtr<IRawElementProviderSimple> link_node_raw =
106 QueryInterfaceFromNode<IRawElementProviderSimple>(link_node);
107 ComPtr<IRawElementProviderSimple> text2_node_raw =
108 QueryInterfaceFromNode<IRawElementProviderSimple>(text2_node);
109
110 ComPtr<AXPlatformNodeWin> root_platform_node;
111 EXPECT_HRESULT_SUCCEEDED(
112 root_node_raw->QueryInterface(IID_PPV_ARGS(&root_platform_node)));
113 ComPtr<AXPlatformNodeWin> link_platform_node;
114 EXPECT_HRESULT_SUCCEEDED(
115 link_node_raw->QueryInterface(IID_PPV_ARGS(&link_platform_node)));
116 ComPtr<AXPlatformNodeWin> text2_platform_node;
117 EXPECT_HRESULT_SUCCEEDED(
118 text2_node_raw->QueryInterface(IID_PPV_ARGS(&text2_platform_node)));
119
120 // Degenerate range created on root node should be:
121 // <>some textmore text
122 ComPtr<ITextRangeProvider> text_range_provider =
123 AXPlatformNodeTextProviderWin::CreateDegenerateRangeAtStart(
124 root_platform_node.Get());
125 SetOwner(owner, text_range_provider.Get());
126 base::win::ScopedBstr text_content;
127 EXPECT_HRESULT_SUCCEEDED(
128 text_range_provider->GetText(-1, text_content.Receive()));
129 EXPECT_EQ(0, wcscmp(text_content.Get(), L""));
130
131 ComPtr<AXPlatformNodeTextRangeProviderWin> actual_range;
132 text_range_provider->QueryInterface(IID_PPV_ARGS(&actual_range));
133 AXNodePosition::AXPositionInstance expected_start, expected_end;
134 expected_start = root_platform_node->GetDelegate()->CreateTextPositionAt(0);
135 expected_end = expected_start->Clone();
136 EXPECT_EQ(*GetStart(actual_range.Get()), *expected_start);
137 EXPECT_EQ(*GetEnd(actual_range.Get()), *expected_end);
138 text_content.Release();
139
140 // Degenerate range created on link node should be:
141 // <>some textmore text
142 text_range_provider =
143 AXPlatformNodeTextProviderWin::CreateDegenerateRangeAtStart(
144 link_platform_node.Get());
145 SetOwner(owner, text_range_provider.Get());
146 EXPECT_HRESULT_SUCCEEDED(
147 text_range_provider->GetText(-1, text_content.Receive()));
148 EXPECT_EQ(0, wcscmp(text_content.Get(), L""));
149 text_range_provider->QueryInterface(IID_PPV_ARGS(&actual_range));
150 EXPECT_EQ(*GetStart(actual_range.Get()), *expected_start);
151 EXPECT_EQ(*GetEnd(actual_range.Get()), *expected_end);
152 text_content.Release();
153
154 // Degenerate range created on more text node should be:
155 // some text<>more text
156 text_range_provider =
157 AXPlatformNodeTextProviderWin::CreateDegenerateRangeAtStart(
158 text2_platform_node.Get());
159 SetOwner(owner, text_range_provider.Get());
160 EXPECT_HRESULT_SUCCEEDED(
161 text_range_provider->GetText(-1, text_content.Receive()));
162 EXPECT_EQ(0, wcscmp(text_content.Get(), L""));
163 text_range_provider->QueryInterface(IID_PPV_ARGS(&actual_range));
164 expected_start = text2_platform_node->GetDelegate()->CreateTextPositionAt(0);
165 expected_end = expected_start->Clone();
166 EXPECT_EQ(*GetStart(actual_range.Get()), *expected_start);
167 EXPECT_EQ(*GetEnd(actual_range.Get()), *expected_end);
168 text_content.Release();
169}
170
171TEST_F(AXPlatformNodeTextProviderTest, ITextProviderRangeFromChild) {
172 AXNodeData text_data;
173 text_data.id = 2;
174 text_data.role = ax::mojom::Role::kStaticText;
175 text_data.SetName("some text");
176
177 AXNodeData empty_text_data;
178 empty_text_data.id = 3;
179 empty_text_data.role = ax::mojom::Role::kStaticText;
180
181 AXNodeData root_data;
182 root_data.id = 1;
183 root_data.role = ax::mojom::Role::kStaticText;
184 root_data.SetName("Document");
185 root_data.child_ids.push_back(2);
186 root_data.child_ids.push_back(3);
187
189 AXTreeData tree_data;
190 tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
191 update.tree_data = tree_data;
192 update.has_tree_data = true;
193 update.root_id = root_data.id;
194 update.nodes.push_back(root_data);
195 update.nodes.push_back(text_data);
196 update.nodes.push_back(empty_text_data);
197
198 Init(update);
199
200 AXNode* root_node = GetRootAsAXNode();
201 AXNode* text_node = root_node->children()[0];
202 AXNode* empty_text_node = root_node->children()[1];
203 AXPlatformNodeWin* owner =
204 static_cast<AXPlatformNodeWin*>(AXPlatformNodeFromNode(root_node));
205 BASE_DCHECK(owner);
206
207 ComPtr<IRawElementProviderSimple> root_node_raw =
208 QueryInterfaceFromNode<IRawElementProviderSimple>(root_node);
209 ComPtr<IRawElementProviderSimple> text_node_raw =
210 QueryInterfaceFromNode<IRawElementProviderSimple>(text_node);
211 ComPtr<IRawElementProviderSimple> empty_text_node_raw =
212 QueryInterfaceFromNode<IRawElementProviderSimple>(empty_text_node);
213
214 // Call RangeFromChild on the root with the text child passed in.
215 ComPtr<ITextProvider> text_provider;
216 EXPECT_HRESULT_SUCCEEDED(
217 root_node_raw->GetPatternProvider(UIA_TextPatternId, &text_provider));
218
219 ComPtr<ITextRangeProvider> text_range_provider;
220 EXPECT_HRESULT_SUCCEEDED(
221 text_provider->RangeFromChild(text_node_raw.Get(), &text_range_provider));
222 SetOwner(owner, text_range_provider.Get());
223
224 base::win::ScopedBstr text_content;
225 EXPECT_HRESULT_SUCCEEDED(
226 text_range_provider->GetText(-1, text_content.Receive()));
227 EXPECT_EQ(0, wcscmp(text_content.Get(), L"some text"));
228
229 // Now test that the reverse relation doesn't return a valid
230 // ITextRangeProvider, and instead returns E_INVALIDARG.
231 EXPECT_HRESULT_SUCCEEDED(
232 text_node_raw->GetPatternProvider(UIA_TextPatternId, &text_provider));
233
235 text_provider->RangeFromChild(root_node_raw.Get(), &text_range_provider));
236
237 // Now test that a child with no text returns a degenerate range.
238 EXPECT_HRESULT_SUCCEEDED(
239 root_node_raw->GetPatternProvider(UIA_TextPatternId, &text_provider));
240
241 EXPECT_HRESULT_SUCCEEDED(text_provider->RangeFromChild(
242 empty_text_node_raw.Get(), &text_range_provider));
243 SetOwner(owner, text_range_provider.Get());
244
245 base::win::ScopedBstr empty_text_content;
246 EXPECT_HRESULT_SUCCEEDED(
247 text_range_provider->GetText(-1, empty_text_content.Receive()));
248 EXPECT_EQ(0, wcscmp(empty_text_content.Get(), L""));
249
250 // Test that passing in an object from a different instance of
251 // IRawElementProviderSimple than that of the valid text provider
252 // returns UIA_E_INVALIDOPERATION.
253 ComPtr<IRawElementProviderSimple> other_root_node_raw;
254 MockIRawElementProviderSimple::CreateMockIRawElementProviderSimple(
255 &other_root_node_raw);
256
257 EXPECT_HRESULT_SUCCEEDED(
258 root_node_raw->GetPatternProvider(UIA_TextPatternId, &text_provider));
259
260 EXPECT_UIA_INVALIDOPERATION(text_provider->RangeFromChild(
261 other_root_node_raw.Get(), &text_range_provider));
262}
263
264TEST_F(AXPlatformNodeTextProviderTest,
265 ITextProviderRangeFromChildMultipleChildren) {
266 const int ROOT_ID = 1;
267 const int DIALOG_ID = 2;
268 const int DIALOG_LABEL_ID = 3;
269 const int DIALOG_DESCRIPTION_ID = 4;
270 const int BUTTON_ID = 5;
271 const int BUTTON_IMG_ID = 6;
272 const int BUTTON_TEXT_ID = 7;
273 const int DIALOG_DETAIL_ID = 8;
274
275 AXNodeData root;
276 root.id = ROOT_ID;
278 root.SetName("Document");
279 root.child_ids = {DIALOG_ID};
280
281 AXNodeData dialog;
282 dialog.id = DIALOG_ID;
283 dialog.role = ax::mojom::Role::kDialog;
284 dialog.child_ids = {DIALOG_LABEL_ID, DIALOG_DESCRIPTION_ID, BUTTON_ID,
285 DIALOG_DETAIL_ID};
286
287 AXNodeData dialog_label;
288 dialog_label.id = DIALOG_LABEL_ID;
289 dialog_label.role = ax::mojom::Role::kStaticText;
290 dialog_label.SetName("Dialog label.");
291
292 AXNodeData dialog_description;
293 dialog_description.id = DIALOG_DESCRIPTION_ID;
294 dialog_description.role = ax::mojom::Role::kStaticText;
295 dialog_description.SetName("Dialog description.");
296
297 AXNodeData button;
298 button.id = BUTTON_ID;
299 button.role = ax::mojom::Role::kButton;
300 button.child_ids = {BUTTON_IMG_ID, BUTTON_TEXT_ID};
301
302 AXNodeData button_img;
303 button_img.id = BUTTON_IMG_ID;
304 button_img.role = ax::mojom::Role::kImage;
305
306 AXNodeData button_text;
307 button_text.id = BUTTON_TEXT_ID;
308 button_text.role = ax::mojom::Role::kStaticText;
309 button_text.SetName("ok.");
310
311 AXNodeData dialog_detail;
312 dialog_detail.id = DIALOG_DETAIL_ID;
313 dialog_detail.role = ax::mojom::Role::kStaticText;
314 dialog_detail.SetName("Some more detail about dialog.");
315
317 AXTreeData tree_data;
318 tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
319 update.tree_data = tree_data;
320 update.has_tree_data = true;
321 update.root_id = ROOT_ID;
322 update.nodes = {root, dialog, dialog_label, dialog_description,
323 button, button_img, button_text, dialog_detail};
324
325 Init(update);
326
327 AXNode* root_node = GetRootAsAXNode();
328 AXNode* dialog_node = root_node->children()[0];
329 AXPlatformNodeWin* owner =
330 static_cast<AXPlatformNodeWin*>(AXPlatformNodeFromNode(root_node));
331 BASE_DCHECK(owner);
332
333 ComPtr<IRawElementProviderSimple> root_node_raw =
334 QueryInterfaceFromNode<IRawElementProviderSimple>(root_node);
335 ComPtr<IRawElementProviderSimple> dialog_node_raw =
336 QueryInterfaceFromNode<IRawElementProviderSimple>(dialog_node);
337
338 // Call RangeFromChild on the root with the dialog child passed in.
339 ComPtr<ITextProvider> text_provider;
340 EXPECT_HRESULT_SUCCEEDED(
341 root_node_raw->GetPatternProvider(UIA_TextPatternId, &text_provider));
342
343 ComPtr<ITextRangeProvider> text_range_provider;
344 EXPECT_HRESULT_SUCCEEDED(text_provider->RangeFromChild(dialog_node_raw.Get(),
345 &text_range_provider));
346 SetOwner(owner, text_range_provider.Get());
347
348 base::win::ScopedBstr text_content;
349 EXPECT_HRESULT_SUCCEEDED(
350 text_range_provider->GetText(-1, text_content.Receive()));
351 EXPECT_EQ(fml::WideStringToUtf16(text_content.Get()),
352 u"Dialog label.Dialog description." + kEmbeddedCharacterAsString +
353 u"ok.Some more detail " + u"about dialog.");
354
355 // Check the reverse relationship that GetEnclosingElement on the text range
356 // gives back the dialog.
357 ComPtr<IRawElementProviderSimple> enclosing_element;
358 EXPECT_HRESULT_SUCCEEDED(
359 text_range_provider->GetEnclosingElement(&enclosing_element));
360 EXPECT_EQ(enclosing_element.Get(), dialog_node_raw.Get());
361}
362
363TEST_F(AXPlatformNodeTextProviderTest, NearestTextIndexToPoint) {
364 AXNodeData text_data;
365 text_data.id = 2;
366 text_data.role = ax::mojom::Role::kInlineTextBox;
367 text_data.SetName("text");
368 // spacing: "t-e-x---t-"
369 text_data.AddIntListAttribute(ax::mojom::IntListAttribute::kCharacterOffsets,
370 {2, 4, 8, 10});
371
372 AXNodeData root_data;
373 root_data.id = 1;
374 root_data.role = ax::mojom::Role::kRootWebArea;
375 root_data.relative_bounds.bounds = gfx::RectF(1, 1, 2, 2);
376 root_data.child_ids.push_back(2);
377
378 Init(root_data, text_data);
379
380 AXNode* root_node = GetRootAsAXNode();
381 AXNode* text_node = root_node->children()[0];
382
383 struct NearestTextIndexTestData {
384 AXNode* node;
385 struct point_offset_expected_index_pair {
386 int point_offset_x;
387 int expected_index;
388 };
389 std::vector<point_offset_expected_index_pair> test_data;
390 };
391 NearestTextIndexTestData nodes[] = {
392 {text_node,
393 {{0, 0}, {2, 0}, {3, 1}, {4, 1}, {5, 2}, {8, 2}, {9, 3}, {10, 3}}},
394 {root_node,
395 {{0, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {8, 0}, {9, 0}, {10, 0}}}};
396 for (auto data : nodes) {
397 if (!data.node->IsText() && !data.node->data().IsTextField()) {
398 continue;
399 }
400 ComPtr<IRawElementProviderSimple> element_provider =
401 QueryInterfaceFromNode<IRawElementProviderSimple>(data.node);
402 ComPtr<ITextProvider> text_provider;
403 EXPECT_HRESULT_SUCCEEDED(element_provider->GetPatternProvider(
404 UIA_TextPatternId, &text_provider));
405 // get internal implementation to access helper for testing
406 ComPtr<AXPlatformNodeTextProviderWin> platform_text_provider;
407 EXPECT_HRESULT_SUCCEEDED(
408 text_provider->QueryInterface(IID_PPV_ARGS(&platform_text_provider)));
409
410 ComPtr<AXPlatformNodeWin> platform_node;
411 EXPECT_HRESULT_SUCCEEDED(
412 element_provider->QueryInterface(IID_PPV_ARGS(&platform_node)));
413
414 for (auto pair : data.test_data) {
415 EXPECT_EQ(pair.expected_index, platform_node->NearestTextIndexToPoint(
416 gfx::Point(pair.point_offset_x, 0)));
417 }
418 }
419}
420
421TEST_F(AXPlatformNodeTextProviderTest, ITextProviderDocumentRange) {
422 AXNodeData text_data;
423 text_data.id = 2;
424 text_data.role = ax::mojom::Role::kStaticText;
425 text_data.SetName("some text");
426
427 AXNodeData root_data;
428 root_data.id = 1;
429 root_data.role = ax::mojom::Role::kStaticText;
430 root_data.SetName("Document");
431 root_data.child_ids.push_back(2);
432
433 Init(root_data, text_data);
434
435 ComPtr<IRawElementProviderSimple> root_node =
436 GetRootIRawElementProviderSimple();
437
438 ComPtr<ITextProvider> text_provider;
439 EXPECT_HRESULT_SUCCEEDED(
440 root_node->GetPatternProvider(UIA_TextPatternId, &text_provider));
441
442 ComPtr<ITextRangeProvider> text_range_provider;
443 EXPECT_HRESULT_SUCCEEDED(
444 text_provider->get_DocumentRange(&text_range_provider));
445}
446
447TEST_F(AXPlatformNodeTextProviderTest,
448 DISABLED_ITextProviderDocumentRangeTrailingIgnored) {
449 // ++1 root
450 // ++++2 kGenericContainer
451 // ++++++3 kStaticText "Hello"
452 // ++++4 kGenericContainer
453 // ++++++5 kGenericContainer
454 // ++++++++6 kStaticText "3.14"
455 // ++++7 kGenericContainer (ignored)
456 // ++++++8 kGenericContainer (ignored)
457 // ++++++++9 kStaticText "ignored"
458 AXNodeData root_1;
459 AXNodeData gc_2;
460 AXNodeData static_text_3;
461 AXNodeData gc_4;
462 AXNodeData gc_5;
463 AXNodeData static_text_6;
464 AXNodeData gc_7_ignored;
465 AXNodeData gc_8_ignored;
466 AXNodeData static_text_9_ignored;
467
468 root_1.id = 1;
469 gc_2.id = 2;
470 static_text_3.id = 3;
471 gc_4.id = 4;
472 gc_5.id = 5;
473 static_text_6.id = 6;
474 gc_7_ignored.id = 7;
475 gc_8_ignored.id = 8;
476 static_text_9_ignored.id = 9;
477
478 root_1.role = ax::mojom::Role::kRootWebArea;
479 root_1.child_ids = {gc_2.id, gc_4.id, gc_7_ignored.id};
480 root_1.SetName("Document");
481
483 gc_2.AddIntListAttribute(ax::mojom::IntListAttribute::kLabelledbyIds,
484 {static_text_3.id});
485 gc_2.child_ids = {static_text_3.id};
486
487 static_text_3.role = ax::mojom::Role::kStaticText;
488 static_text_3.SetName("Hello");
489
491 gc_4.AddIntListAttribute(ax::mojom::IntListAttribute::kLabelledbyIds,
492 {gc_5.id});
493 gc_4.child_ids = {gc_5.id};
494
496 gc_5.child_ids = {static_text_6.id};
497
498 static_text_6.role = ax::mojom::Role::kStaticText;
499 static_text_6.SetName("3.14");
500
501 gc_7_ignored.role = ax::mojom::Role::kGenericContainer;
502 gc_7_ignored.child_ids = {gc_8_ignored.id};
503 gc_7_ignored.AddState(ax::mojom::State::kIgnored);
504
505 gc_8_ignored.role = ax::mojom::Role::kGenericContainer;
506 gc_8_ignored.child_ids = {static_text_9_ignored.id};
507 gc_8_ignored.AddState(ax::mojom::State::kIgnored);
508
509 static_text_9_ignored.role = ax::mojom::Role::kStaticText;
510 static_text_9_ignored.SetName("ignored");
511 static_text_9_ignored.AddState(ax::mojom::State::kIgnored);
512
514 AXTreeData tree_data;
515 tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
516 update.tree_data = tree_data;
517 update.has_tree_data = true;
518 update.root_id = root_1.id;
519 update.nodes = {root_1, gc_2, static_text_3,
520 gc_4, gc_5, static_text_6,
521 gc_7_ignored, gc_8_ignored, static_text_9_ignored};
522
523 Init(update);
524
525 ComPtr<IRawElementProviderSimple> root_node =
526 GetRootIRawElementProviderSimple();
527
528 ComPtr<ITextProvider> text_provider;
529 EXPECT_HRESULT_SUCCEEDED(
530 root_node->GetPatternProvider(UIA_TextPatternId, &text_provider));
531
532 ComPtr<ITextRangeProvider> text_range_provider;
533 EXPECT_HRESULT_SUCCEEDED(
534 text_provider->get_DocumentRange(&text_range_provider));
535
536 ComPtr<AXPlatformNodeTextRangeProviderWin> text_range;
537 text_range_provider->QueryInterface(IID_PPV_ARGS(&text_range));
538
539 ComPtr<ITextProvider> root_text_provider;
540 EXPECT_HRESULT_SUCCEEDED(
541 root_node->GetPatternProvider(UIA_TextPatternId, &root_text_provider));
542 ComPtr<AXPlatformNodeTextProviderWin> root_platform_node;
543 root_text_provider->QueryInterface(IID_PPV_ARGS(&root_platform_node));
544 AXPlatformNodeWin* owner = GetOwner(root_platform_node.Get());
545
546 AXNodePosition::AXPositionInstance expected_start =
547 owner->GetDelegate()->CreateTextPositionAt(0)->AsLeafTextPosition();
548 AXNodePosition::AXPositionInstance expected_end =
549 owner->GetDelegate()
550 ->CreateTextPositionAt(0)
551 ->CreatePositionAtEndOfAnchor();
552 expected_end = expected_end->AsLeafTextPosition();
553 EXPECT_EQ(*GetStart(text_range.Get()), *expected_start);
554 EXPECT_EQ(*GetEnd(text_range.Get()), *expected_end);
555}
556
557TEST_F(AXPlatformNodeTextProviderTest, ITextProviderDocumentRangeNested) {
558 AXNodeData text_data;
559 text_data.id = 3;
560 text_data.role = ax::mojom::Role::kStaticText;
561 text_data.SetName("some text");
562
563 AXNodeData paragraph_data;
564 paragraph_data.id = 2;
565 paragraph_data.role = ax::mojom::Role::kParagraph;
566 paragraph_data.child_ids.push_back(3);
567
568 AXNodeData root_data;
569 root_data.id = 1;
570 root_data.role = ax::mojom::Role::kStaticText;
571 root_data.SetName("Document");
572 root_data.child_ids.push_back(2);
573
574 Init(root_data, paragraph_data, text_data);
575
576 ComPtr<IRawElementProviderSimple> root_node =
577 GetRootIRawElementProviderSimple();
578
579 ComPtr<ITextProvider> text_provider;
580 EXPECT_HRESULT_SUCCEEDED(
581 root_node->GetPatternProvider(UIA_TextPatternId, &text_provider));
582
583 ComPtr<ITextRangeProvider> text_range_provider;
584 EXPECT_HRESULT_SUCCEEDED(
585 text_provider->get_DocumentRange(&text_range_provider));
586}
587
588TEST_F(AXPlatformNodeTextProviderTest, ITextProviderSupportedSelection) {
589 AXNodeData text_data;
590 text_data.id = 2;
591 text_data.role = ax::mojom::Role::kStaticText;
592 text_data.SetName("some text");
593
594 AXNodeData root_data;
595 root_data.id = 1;
596 root_data.role = ax::mojom::Role::kStaticText;
597 root_data.SetName("Document");
598 root_data.child_ids.push_back(2);
599
600 Init(root_data, text_data);
601
602 ComPtr<IRawElementProviderSimple> root_node =
603 GetRootIRawElementProviderSimple();
604
605 ComPtr<ITextProvider> text_provider;
606 EXPECT_HRESULT_SUCCEEDED(
607 root_node->GetPatternProvider(UIA_TextPatternId, &text_provider));
608
609 SupportedTextSelection text_selection_mode;
610 EXPECT_HRESULT_SUCCEEDED(
611 text_provider->get_SupportedTextSelection(&text_selection_mode));
612 EXPECT_EQ(text_selection_mode, SupportedTextSelection_Single);
613}
614
615TEST_F(AXPlatformNodeTextProviderTest, ITextProviderGetSelection) {
616 AXNodeData text_data;
617 text_data.id = 2;
618 text_data.role = ax::mojom::Role::kStaticText;
619 text_data.SetName("some text");
620
621 AXNodeData textbox_data;
622 textbox_data.id = 3;
623 textbox_data.role = ax::mojom::Role::kInlineTextBox;
624 textbox_data.SetName("textbox text");
625 textbox_data.AddState(ax::mojom::State::kEditable);
626
627 AXNodeData nonatomic_textfield_data;
628 nonatomic_textfield_data.id = 4;
629 nonatomic_textfield_data.role = ax::mojom::Role::kGroup;
630 nonatomic_textfield_data.child_ids = {5};
631
632 AXNodeData text_child_data;
633 text_child_data.id = 5;
634 text_child_data.role = ax::mojom::Role::kStaticText;
635 text_child_data.SetName("text");
636
637 AXNodeData root_data;
638 root_data.id = 1;
639 root_data.role = ax::mojom::Role::kStaticText;
640 root_data.SetName("Document");
641 root_data.child_ids = {2, 3, 4};
642
644 AXTreeData tree_data;
645 tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
646 update.tree_data = tree_data;
647 update.has_tree_data = true;
648 update.root_id = root_data.id;
649 update.nodes = {root_data, text_data, textbox_data, nonatomic_textfield_data,
650 text_child_data};
651 Init(update);
652
653 ComPtr<IRawElementProviderSimple> root_node =
654 GetRootIRawElementProviderSimple();
655
656 ComPtr<ITextProvider> root_text_provider;
657 EXPECT_HRESULT_SUCCEEDED(
658 root_node->GetPatternProvider(UIA_TextPatternId, &root_text_provider));
659
661 root_text_provider->GetSelection(selections.Receive());
662 ASSERT_EQ(nullptr, selections.Get());
663
664 ComPtr<AXPlatformNodeTextProviderWin> root_platform_node;
665 root_text_provider->QueryInterface(IID_PPV_ARGS(&root_platform_node));
666
667 AXPlatformNodeWin* owner = GetOwner(root_platform_node.Get());
668 AXTreeData& selected_tree_data =
669 const_cast<AXTreeData&>(owner->GetDelegate()->GetTreeData());
670 selected_tree_data.sel_focus_object_id = 2;
671 selected_tree_data.sel_anchor_object_id = 2;
672 selected_tree_data.sel_anchor_offset = 0;
673 selected_tree_data.sel_focus_offset = 4;
674
675 root_text_provider->GetSelection(selections.Receive());
676 ASSERT_NE(nullptr, selections.Get());
677
678 LONG ubound;
679 EXPECT_HRESULT_SUCCEEDED(SafeArrayGetUBound(selections.Get(), 1, &ubound));
680 EXPECT_EQ(0, ubound);
681 LONG lbound;
682 EXPECT_HRESULT_SUCCEEDED(SafeArrayGetLBound(selections.Get(), 1, &lbound));
683 EXPECT_EQ(0, lbound);
684
685 LONG index = 0;
686 ComPtr<ITextRangeProvider> text_range_provider;
687 EXPECT_HRESULT_SUCCEEDED(SafeArrayGetElement(
688 selections.Get(), &index, static_cast<void**>(&text_range_provider)));
689 SetOwner(owner, text_range_provider.Get());
690
691 base::win::ScopedBstr text_content;
692 EXPECT_HRESULT_SUCCEEDED(
693 text_range_provider->GetText(-1, text_content.Receive()));
694 EXPECT_EQ(0, wcscmp(text_content.Get(), L"some"));
695 text_content.Reset();
696 selections.Reset();
697 text_range_provider.Reset();
698
699 // Verify that start and end are appropriately swapped when sel_anchor_offset
700 // is greater than sel_focus_offset
701 selected_tree_data.sel_focus_object_id = 2;
702 selected_tree_data.sel_anchor_object_id = 2;
703 selected_tree_data.sel_anchor_offset = 4;
704 selected_tree_data.sel_focus_offset = 0;
705
706 root_text_provider->GetSelection(selections.Receive());
707 ASSERT_NE(nullptr, selections.Get());
708
709 EXPECT_HRESULT_SUCCEEDED(SafeArrayGetUBound(selections.Get(), 1, &ubound));
710 EXPECT_EQ(0, ubound);
711 EXPECT_HRESULT_SUCCEEDED(SafeArrayGetLBound(selections.Get(), 1, &lbound));
712 EXPECT_EQ(0, lbound);
713
714 EXPECT_HRESULT_SUCCEEDED(SafeArrayGetElement(
715 selections.Get(), &index, static_cast<void**>(&text_range_provider)));
716 SetOwner(owner, text_range_provider.Get());
717
718 EXPECT_HRESULT_SUCCEEDED(
719 text_range_provider->GetText(-1, text_content.Receive()));
720 EXPECT_EQ(0, wcscmp(text_content.Get(), L"some"));
721 text_content.Reset();
722 selections.Reset();
723 text_range_provider.Reset();
724
725 // Verify that text ranges at an insertion point returns a degenerate (empty)
726 // text range via textbox with sel_anchor_offset equal to sel_focus_offset
727 selected_tree_data.sel_focus_object_id = 3;
728 selected_tree_data.sel_anchor_object_id = 3;
729 selected_tree_data.sel_anchor_offset = 1;
730 selected_tree_data.sel_focus_offset = 1;
731
732 AXNode* text_edit_node = GetRootAsAXNode()->children()[1];
733
734 ComPtr<IRawElementProviderSimple> text_edit_com =
735 QueryInterfaceFromNode<IRawElementProviderSimple>(text_edit_node);
736
737 ComPtr<ITextProvider> text_edit_provider;
738 EXPECT_HRESULT_SUCCEEDED(text_edit_com->GetPatternProvider(
739 UIA_TextPatternId, &text_edit_provider));
740
741 selections.Reset();
742 EXPECT_HRESULT_SUCCEEDED(
743 text_edit_provider->GetSelection(selections.Receive()));
744 EXPECT_NE(nullptr, selections.Get());
745
746 EXPECT_HRESULT_SUCCEEDED(SafeArrayGetUBound(selections.Get(), 1, &ubound));
747 EXPECT_EQ(0, ubound);
748 EXPECT_HRESULT_SUCCEEDED(SafeArrayGetLBound(selections.Get(), 1, &lbound));
749 EXPECT_EQ(0, lbound);
750
751 ComPtr<ITextRangeProvider> text_edit_range_provider;
752 EXPECT_HRESULT_SUCCEEDED(
753 SafeArrayGetElement(selections.Get(), &index,
754 static_cast<void**>(&text_edit_range_provider)));
755 SetOwner(owner, text_edit_range_provider.Get());
756 EXPECT_HRESULT_SUCCEEDED(
757 text_edit_range_provider->GetText(-1, text_content.Receive()));
758 EXPECT_EQ(0U, text_content.Length());
759 text_content.Reset();
760 selections.Reset();
761 text_edit_range_provider.Reset();
762
763 // Verify selections that span multiple nodes
764 selected_tree_data.sel_focus_object_id = 2;
765 selected_tree_data.sel_focus_offset = 0;
766 selected_tree_data.sel_anchor_object_id = 3;
767 selected_tree_data.sel_anchor_offset = 12;
768
769 root_text_provider->GetSelection(selections.Receive());
770 ASSERT_NE(nullptr, selections.Get());
771
772 EXPECT_HRESULT_SUCCEEDED(SafeArrayGetUBound(selections.Get(), 1, &ubound));
773 EXPECT_EQ(0, ubound);
774 EXPECT_HRESULT_SUCCEEDED(SafeArrayGetLBound(selections.Get(), 1, &lbound));
775 EXPECT_EQ(0, lbound);
776
777 EXPECT_HRESULT_SUCCEEDED(SafeArrayGetElement(
778 selections.Get(), &index, static_cast<void**>(&text_range_provider)));
779
780 SetOwner(owner, text_range_provider.Get());
781 EXPECT_HRESULT_SUCCEEDED(
782 text_range_provider->GetText(-1, text_content.Receive()));
783 EXPECT_EQ(0, wcscmp(text_content.Get(), L"some texttextbox text"));
784 text_content.Reset();
785 selections.Reset();
786 text_range_provider.Reset();
787
788 // Verify SAFEARRAY value for degenerate selection.
789 selected_tree_data.sel_focus_object_id = 2;
790 selected_tree_data.sel_anchor_object_id = 2;
791 selected_tree_data.sel_anchor_offset = 1;
792 selected_tree_data.sel_focus_offset = 1;
793
794 root_text_provider->GetSelection(selections.Receive());
795 ASSERT_NE(nullptr, selections.Get());
796
797 EXPECT_HRESULT_SUCCEEDED(SafeArrayGetUBound(selections.Get(), 1, &ubound));
798 EXPECT_EQ(0, ubound);
799 EXPECT_HRESULT_SUCCEEDED(SafeArrayGetLBound(selections.Get(), 1, &lbound));
800 EXPECT_EQ(0, lbound);
801
802 EXPECT_HRESULT_SUCCEEDED(SafeArrayGetElement(
803 selections.Get(), &index, static_cast<void**>(&text_range_provider)));
804
805 SetOwner(owner, text_range_provider.Get());
806 EXPECT_HRESULT_SUCCEEDED(
807 text_range_provider->GetText(-1, text_content.Receive()));
808 EXPECT_EQ(0, wcscmp(text_content.Get(), L""));
809 text_content.Reset();
810 selections.Reset();
811 text_range_provider.Reset();
812
813 // Removed testing logic for non-atomic text fields as we do not have this
814 // role.
815
816 // Now delete the tree (which will delete the associated elements) and verify
817 // that UIA_E_ELEMENTNOTAVAILABLE is returned when calling GetSelection on
818 // a dead element
819 DestroyTree();
820
821 EXPECT_EQ(static_cast<HRESULT>(UIA_E_ELEMENTNOTAVAILABLE),
822 text_edit_provider->GetSelection(selections.Receive()));
823}
824
825TEST_F(AXPlatformNodeTextProviderTest, ITextProviderGetActiveComposition) {
826 AXNodeData text_data;
827 text_data.id = 2;
828 text_data.role = ax::mojom::Role::kStaticText;
829 text_data.SetName("some text");
830
831 AXNodeData root_data;
832 root_data.id = 1;
833 root_data.role = ax::mojom::Role::kStaticText;
834 root_data.SetName("Document");
835 root_data.child_ids.push_back(2);
836
838 AXTreeData tree_data;
839 tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
840 update.tree_data = tree_data;
841 update.has_tree_data = true;
842 update.root_id = root_data.id;
843 update.nodes.push_back(root_data);
844 update.nodes.push_back(text_data);
845 Init(update);
846
847 ComPtr<IRawElementProviderSimple> root_node =
848 GetRootIRawElementProviderSimple();
849
850 ComPtr<ITextProvider> root_text_provider;
851 EXPECT_HRESULT_SUCCEEDED(
852 root_node->GetPatternProvider(UIA_TextPatternId, &root_text_provider));
853
854 ComPtr<ITextEditProvider> root_text_edit_provider;
855 EXPECT_HRESULT_SUCCEEDED(root_node->GetPatternProvider(
856 UIA_TextEditPatternId, &root_text_edit_provider));
857
858 ComPtr<ITextRangeProvider> text_range_provider;
859 root_text_edit_provider->GetActiveComposition(&text_range_provider);
860 ASSERT_EQ(nullptr, text_range_provider);
861
862 ComPtr<AXPlatformNodeTextProviderWin> root_platform_node;
863 root_text_provider->QueryInterface(IID_PPV_ARGS(&root_platform_node));
864
865 AXActionData action_data;
866 action_data.action = ax::mojom::Action::kFocus;
867 action_data.target_node_id = 1;
868 AXPlatformNodeWin* owner = GetOwner(root_platform_node.Get());
869 owner->GetDelegate()->AccessibilityPerformAction(action_data);
870 const std::u16string active_composition_text = u"a";
871 owner->OnActiveComposition(gfx::Range(0, 1), active_composition_text, false);
872
873 root_text_edit_provider->GetActiveComposition(&text_range_provider);
874 ASSERT_NE(nullptr, text_range_provider);
875 ComPtr<AXPlatformNodeTextRangeProviderWin> actual_range;
876 AXNodePosition::AXPositionInstance expected_start =
877 owner->GetDelegate()->CreateTextPositionAt(0);
878 AXNodePosition::AXPositionInstance expected_end =
879 owner->GetDelegate()->CreateTextPositionAt(1);
880 text_range_provider->QueryInterface(IID_PPV_ARGS(&actual_range));
881 EXPECT_EQ(*GetStart(actual_range.Get()), *expected_start);
882 EXPECT_EQ(*GetEnd(actual_range.Get()), *expected_end);
883}
884
885TEST_F(AXPlatformNodeTextProviderTest, ITextProviderGetConversionTarget) {
886 AXNodeData text_data;
887 text_data.id = 2;
888 text_data.role = ax::mojom::Role::kStaticText;
889 text_data.SetName("some text");
890
891 AXNodeData root_data;
892 root_data.id = 1;
893 root_data.role = ax::mojom::Role::kStaticText;
894 root_data.SetName("Document");
895 root_data.child_ids.push_back(2);
896
898 AXTreeData tree_data;
899 tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
900 update.tree_data = tree_data;
901 update.has_tree_data = true;
902 update.root_id = root_data.id;
903 update.nodes.push_back(root_data);
904 update.nodes.push_back(text_data);
905 Init(update);
906
907 ComPtr<IRawElementProviderSimple> root_node =
908 GetRootIRawElementProviderSimple();
909
910 ComPtr<ITextProvider> root_text_provider;
911 EXPECT_HRESULT_SUCCEEDED(
912 root_node->GetPatternProvider(UIA_TextPatternId, &root_text_provider));
913
914 ComPtr<ITextEditProvider> root_text_edit_provider;
915 EXPECT_HRESULT_SUCCEEDED(root_node->GetPatternProvider(
916 UIA_TextEditPatternId, &root_text_edit_provider));
917
918 ComPtr<ITextRangeProvider> text_range_provider;
919 root_text_edit_provider->GetConversionTarget(&text_range_provider);
920 ASSERT_EQ(nullptr, text_range_provider);
921
922 ComPtr<AXPlatformNodeTextProviderWin> root_platform_node;
923 root_text_provider->QueryInterface(IID_PPV_ARGS(&root_platform_node));
924
925 AXActionData action_data;
926 action_data.action = ax::mojom::Action::kFocus;
927 action_data.target_node_id = 1;
928 AXPlatformNodeWin* owner = GetOwner(root_platform_node.Get());
929 owner->GetDelegate()->AccessibilityPerformAction(action_data);
930 const std::u16string active_composition_text = u"a";
931 owner->OnActiveComposition(gfx::Range(0, 1), active_composition_text, false);
932
933 root_text_edit_provider->GetConversionTarget(&text_range_provider);
934 ASSERT_NE(nullptr, text_range_provider);
935 ComPtr<AXPlatformNodeTextRangeProviderWin> actual_range;
936 AXNodePosition::AXPositionInstance expected_start =
937 owner->GetDelegate()->CreateTextPositionAt(0);
938 AXNodePosition::AXPositionInstance expected_end =
939 owner->GetDelegate()->CreateTextPositionAt(1);
940 text_range_provider->QueryInterface(IID_PPV_ARGS(&actual_range));
941 EXPECT_EQ(*GetStart(actual_range.Get()), *expected_start);
942 EXPECT_EQ(*GetEnd(actual_range.Get()), *expected_end);
943}
944
945} // namespace ui
#define EXPECT_UIA_INVALIDOPERATION(expr)
void Reset(BSTR bstr=nullptr)
size_t Length() const
void Reset(SAFEARRAY *safearray=nullptr)
TEST_F(FlGnomeSettingsTest, ClockFormat)
void Init()
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
std::u16string WideStringToUtf16(const std::wstring_view str)
AXTreeUpdateBase< AXNodeData, AXTreeData > AXTreeUpdate
#define BASE_DCHECK(condition)
Definition logging.h:63
long LONG

◆ EXPECT_UIA_INVALIDOPERATION

#define EXPECT_UIA_INVALIDOPERATION (   expr)     EXPECT_EQ(static_cast<HRESULT>(UIA_E_INVALIDOPERATION), (expr))