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
5 #include "ui/accessibility/platform/ax_platform_node_win_unittest.h"
6
7 #include "base/win/scoped_bstr.h"
8 #include "ui/accessibility/platform/ax_fragment_root_win.h"
9 #include "ui/accessibility/platform/ax_platform_node_textchildprovider_win.h"
10 #include "ui/accessibility/platform/ax_platform_node_textprovider_win.h"
11 #include "ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h"
12
13 using Microsoft::WRL::ComPtr;
14
15 namespace ui {
16
17 class AXPlatformNodeTextChildProviderTest : public AXPlatformNodeWinTest {
18 protected:
19 // Construct an accessibility tree for testing ITextChildProvider resolution
20 // from various positions in the tree. The following tree configuration
21 // is constructed:
22 //
23 // root_________________________________________________
24 // | |
25 // nontext_child_of_root______ text_child_of_root
26 // | | |
27 // nontext_child_of_nontext text_child_of_nontext text_child_of_text
28 //
29 // nontext leaf elements are considered as embedded objects and expose a
30 // character to allow the text pattern navigation to work with them too.
31 // Because of that, a nontext leaf element is treated as a text element.
SetUp()32 void SetUp() override {
33 ui::AXNodeData root;
34 root.id = 1;
35 root.role = ax::mojom::Role::kRootWebArea;
36
37 ui::AXNodeData nontext_child_of_root;
38 nontext_child_of_root.id = 2;
39 nontext_child_of_root.role = ax::mojom::Role::kGroup;
40 nontext_child_of_root.SetName("non text child of root.");
41 root.child_ids.push_back(nontext_child_of_root.id);
42
43 ui::AXNodeData text_child_of_root;
44 text_child_of_root.id = 3;
45 text_child_of_root.role = ax::mojom::Role::kStaticText;
46 text_child_of_root.SetName("text child of root.");
47 root.child_ids.push_back(text_child_of_root.id);
48
49 ui::AXNodeData nontext_child_of_nontext;
50 nontext_child_of_nontext.id = 4;
51 nontext_child_of_nontext.role = ax::mojom::Role::kGroup;
52 nontext_child_of_nontext.SetName("nontext child of nontext.");
53 nontext_child_of_root.child_ids.push_back(nontext_child_of_nontext.id);
54
55 ui::AXNodeData text_child_of_nontext;
56 text_child_of_nontext.id = 5;
57 text_child_of_nontext.role = ax::mojom::Role::kStaticText;
58 text_child_of_nontext.SetName("text child of nontext.");
59 nontext_child_of_root.child_ids.push_back(text_child_of_nontext.id);
60
61 ui::AXNodeData text_child_of_text;
62 text_child_of_text.id = 6;
63 text_child_of_text.role = ax::mojom::Role::kStaticText;
64 text_child_of_text.SetName("text child of text.");
65 text_child_of_root.child_ids.push_back(text_child_of_text.id);
66
67 ui::AXTreeUpdate update;
68 ui::AXTreeData tree_data;
69 tree_data.tree_id = ui::AXTreeID::CreateNewAXTreeID();
70 update.tree_data = tree_data;
71 update.has_tree_data = true;
72 update.root_id = root.id;
73 update.nodes = {root,
74 nontext_child_of_root,
75 text_child_of_root,
76 nontext_child_of_nontext,
77 text_child_of_nontext,
78 text_child_of_text};
79
80 Init(update);
81
82 AXNode* root_node = GetRootAsAXNode();
83 AXNode* nontext_child_of_root_node = root_node->children()[0];
84 AXNode* text_child_of_root_node = root_node->children()[1];
85 AXNode* nontext_child_of_nontext_node =
86 nontext_child_of_root_node->children()[0];
87 AXNode* text_child_of_nontext_node =
88 nontext_child_of_root_node->children()[1];
89 AXNode* text_child_of_text_node = text_child_of_root_node->children()[0];
90
91 InitITextChildProvider(root_node, root_provider_raw_,
92 root_text_child_provider_);
93 InitITextChildProvider(nontext_child_of_root_node,
94 nontext_child_of_root_provider_raw_,
95 nontext_child_of_root_text_child_provider_);
96 InitITextChildProvider(text_child_of_root_node,
97 text_child_of_root_text_provider_raw_,
98 text_child_of_root_text_child_provider_);
99 InitITextChildProvider(nontext_child_of_nontext_node,
100 nontext_child_of_nontext_text_provider_raw_,
101 nontext_child_of_nontext_text_child_provider_);
102 InitITextChildProvider(text_child_of_nontext_node,
103 text_child_of_nontext_text_provider_raw_,
104 text_child_of_nontext_text_child_provider_);
105 InitITextChildProvider(text_child_of_text_node,
106 text_child_of_text_text_provider_raw_,
107 text_child_of_text_text_child_provider_);
108 }
109
InitITextChildProvider(AXNode * node,ComPtr<IRawElementProviderSimple> & raw_element_provider,ComPtr<ITextChildProvider> & text_child_provider)110 void InitITextChildProvider(
111 AXNode* node,
112 ComPtr<IRawElementProviderSimple>& raw_element_provider,
113 ComPtr<ITextChildProvider>& text_child_provider) {
114 raw_element_provider =
115 QueryInterfaceFromNode<IRawElementProviderSimple>(node);
116
117 EXPECT_HRESULT_SUCCEEDED(raw_element_provider->GetPatternProvider(
118 UIA_TextChildPatternId, &text_child_provider));
119
120 // If the element does not support ITextChildProvider, create one anyways
121 // for testing purposes.
122 if (!text_child_provider) {
123 ui::AXPlatformNodeWin* platform_node =
124 (ui::AXPlatformNodeWin*)raw_element_provider.Get();
125
126 ComPtr<ITextChildProvider> new_child_provider =
127 ui::AXPlatformNodeTextChildProviderWin::Create(platform_node);
128 new_child_provider->QueryInterface(IID_PPV_ARGS(&text_child_provider));
129 }
130 }
131
132 ComPtr<IRawElementProviderSimple> root_provider_raw_;
133 ComPtr<IRawElementProviderSimple> nontext_child_of_root_provider_raw_;
134 ComPtr<IRawElementProviderSimple> text_child_of_root_text_provider_raw_;
135 ComPtr<IRawElementProviderSimple> nontext_child_of_nontext_text_provider_raw_;
136 ComPtr<IRawElementProviderSimple> text_child_of_nontext_text_provider_raw_;
137 ComPtr<IRawElementProviderSimple> text_child_of_text_text_provider_raw_;
138
139 ComPtr<ITextChildProvider> root_text_child_provider_;
140 ComPtr<ITextChildProvider> nontext_child_of_root_text_child_provider_;
141 ComPtr<ITextChildProvider> text_child_of_root_text_child_provider_;
142 ComPtr<ITextChildProvider> nontext_child_of_nontext_text_child_provider_;
143 ComPtr<ITextChildProvider> text_child_of_nontext_text_child_provider_;
144 ComPtr<ITextChildProvider> text_child_of_text_text_child_provider_;
145 };
146
147 // ITextChildProvider::TextContainer Tests
148 //
149 // For each possible position in the tree verify:
150 // 1) A text container can/cannot be retrieved if an ancestor does/doesn't
151 // support the UIA Text control pattern.
152 // 2) Any retrieved text container is the nearest ancestor text container.
153 // 3) A Text control can in fact be retrieved from any retrieved text
154 // container.
155
TEST_F(AXPlatformNodeTextChildProviderTest,ITextChildProviderTextContainerFromRoot)156 TEST_F(AXPlatformNodeTextChildProviderTest,
157 ITextChildProviderTextContainerFromRoot) {
158 ComPtr<IRawElementProviderSimple> text_container;
159 EXPECT_HRESULT_SUCCEEDED(
160 root_text_child_provider_->get_TextContainer(&text_container));
161 ASSERT_EQ(nullptr, text_container.Get());
162 }
163
TEST_F(AXPlatformNodeTextChildProviderTest,ITextChildProviderTextContainerFromNontextChildOfRoot)164 TEST_F(AXPlatformNodeTextChildProviderTest,
165 ITextChildProviderTextContainerFromNontextChildOfRoot) {
166 ComPtr<IRawElementProviderSimple> text_container;
167 EXPECT_HRESULT_SUCCEEDED(
168 nontext_child_of_root_text_child_provider_->get_TextContainer(
169 &text_container));
170 ASSERT_NE(nullptr, text_container.Get());
171
172 EXPECT_EQ(root_provider_raw_.Get(), text_container.Get());
173
174 ComPtr<IUnknown> pattern_provider;
175 ComPtr<ITextProvider> text_container_text_provider;
176 text_container->GetPatternProvider(UIA_TextPatternId, &pattern_provider);
177 ASSERT_NE(nullptr, pattern_provider.Get());
178 EXPECT_HRESULT_SUCCEEDED(pattern_provider.As(&text_container_text_provider));
179 }
180
TEST_F(AXPlatformNodeTextChildProviderTest,ITextChildProviderTextContainerFromTextChildOfRoot)181 TEST_F(AXPlatformNodeTextChildProviderTest,
182 ITextChildProviderTextContainerFromTextChildOfRoot) {
183 ComPtr<IRawElementProviderSimple> text_container;
184 EXPECT_HRESULT_SUCCEEDED(
185 text_child_of_root_text_child_provider_->get_TextContainer(
186 &text_container));
187 ASSERT_NE(nullptr, text_container.Get());
188
189 EXPECT_EQ(root_provider_raw_.Get(), text_container.Get());
190
191 ComPtr<IUnknown> pattern_provider;
192 ComPtr<ITextProvider> text_container_text_provider;
193 text_container->GetPatternProvider(UIA_TextPatternId, &pattern_provider);
194 ASSERT_NE(nullptr, pattern_provider.Get());
195 EXPECT_HRESULT_SUCCEEDED(pattern_provider.As(&text_container_text_provider));
196 }
197
TEST_F(AXPlatformNodeTextChildProviderTest,ITextChildProviderTextContainerFromNontextChildOfNontext)198 TEST_F(AXPlatformNodeTextChildProviderTest,
199 ITextChildProviderTextContainerFromNontextChildOfNontext) {
200 ComPtr<IRawElementProviderSimple> text_container;
201 EXPECT_HRESULT_SUCCEEDED(
202 nontext_child_of_nontext_text_child_provider_->get_TextContainer(
203 &text_container));
204 ASSERT_NE(nullptr, text_container.Get());
205
206 EXPECT_EQ(root_provider_raw_.Get(), text_container.Get());
207
208 ComPtr<IUnknown> pattern_provider;
209 ComPtr<ITextProvider> text_container_text_provider;
210 text_container->GetPatternProvider(UIA_TextPatternId, &pattern_provider);
211 ASSERT_NE(nullptr, pattern_provider.Get());
212 EXPECT_HRESULT_SUCCEEDED(pattern_provider.As(&text_container_text_provider));
213 }
214
TEST_F(AXPlatformNodeTextChildProviderTest,ITextChildProviderTextContainerFromTextChildOfNontext)215 TEST_F(AXPlatformNodeTextChildProviderTest,
216 ITextChildProviderTextContainerFromTextChildOfNontext) {
217 ComPtr<IRawElementProviderSimple> text_container;
218 EXPECT_HRESULT_SUCCEEDED(
219 text_child_of_nontext_text_child_provider_->get_TextContainer(
220 &text_container));
221 ASSERT_NE(nullptr, text_container.Get());
222
223 EXPECT_EQ(root_provider_raw_.Get(), text_container.Get());
224
225 ComPtr<IUnknown> pattern_provider;
226 ComPtr<ITextProvider> text_container_text_provider;
227 text_container->GetPatternProvider(UIA_TextPatternId, &pattern_provider);
228 ASSERT_NE(nullptr, pattern_provider.Get());
229 EXPECT_HRESULT_SUCCEEDED(pattern_provider.As(&text_container_text_provider));
230 }
231
TEST_F(AXPlatformNodeTextChildProviderTest,ITextChildProviderTextContainerFromTextChildOfText)232 TEST_F(AXPlatformNodeTextChildProviderTest,
233 ITextChildProviderTextContainerFromTextChildOfText) {
234 ComPtr<IRawElementProviderSimple> text_container;
235 EXPECT_HRESULT_SUCCEEDED(
236 text_child_of_text_text_child_provider_->get_TextContainer(
237 &text_container));
238 ASSERT_NE(nullptr, text_container.Get());
239
240 EXPECT_EQ(text_child_of_root_text_provider_raw_.Get(), text_container.Get());
241
242 ComPtr<IUnknown> pattern_provider;
243 ComPtr<ITextProvider> text_container_text_provider;
244 text_container->GetPatternProvider(UIA_TextPatternId, &pattern_provider);
245 ASSERT_NE(nullptr, pattern_provider.Get());
246 EXPECT_HRESULT_SUCCEEDED(pattern_provider.As(&text_container_text_provider));
247 }
248
249 // ITextChildProvider::TextRange Tests
250 //
251 // For each possible position in the tree verify:
252 // 1) A text range can/cannot be retrieved if an ancestor does/doesn't
253 // support the UIA Text control pattern.
254 // 2) Any retrieved text range encloses the child element.
TEST_F(AXPlatformNodeTextChildProviderTest,ITextChildProviderTextRangeFromRoot)255 TEST_F(AXPlatformNodeTextChildProviderTest,
256 ITextChildProviderTextRangeFromRoot) {
257 ComPtr<ITextRangeProvider> text_range_provider;
258 EXPECT_HRESULT_SUCCEEDED(
259 root_text_child_provider_->get_TextRange(&text_range_provider));
260 EXPECT_EQ(nullptr, text_range_provider.Get());
261 }
262
TEST_F(AXPlatformNodeTextChildProviderTest,ITextChildProviderTextRangeFromNontextChildOfRoot)263 TEST_F(AXPlatformNodeTextChildProviderTest,
264 ITextChildProviderTextRangeFromNontextChildOfRoot) {
265 ComPtr<ITextRangeProvider> text_range_provider;
266 EXPECT_HRESULT_SUCCEEDED(
267 nontext_child_of_root_text_child_provider_->get_TextRange(
268 &text_range_provider));
269 ASSERT_NE(nullptr, text_range_provider.Get());
270
271 base::win::ScopedBstr text_content;
272 EXPECT_HRESULT_SUCCEEDED(
273 text_range_provider->GetText(-1, text_content.Receive()));
274 EXPECT_EQ(
275 0,
276 wcscmp(text_content.Get(),
277 (kEmbeddedCharacterAsString + L"text child of nontext.").c_str()));
278
279 ComPtr<IRawElementProviderSimple> enclosing_element;
280 text_range_provider->GetEnclosingElement(&enclosing_element);
281 EXPECT_EQ(nontext_child_of_root_provider_raw_.Get(), enclosing_element.Get());
282 }
283
TEST_F(AXPlatformNodeTextChildProviderTest,ITextChildProviderTextRangeFromTextChildOfRoot)284 TEST_F(AXPlatformNodeTextChildProviderTest,
285 ITextChildProviderTextRangeFromTextChildOfRoot) {
286 ComPtr<ITextRangeProvider> text_range_provider;
287 EXPECT_HRESULT_SUCCEEDED(
288 text_child_of_root_text_child_provider_->get_TextRange(
289 &text_range_provider));
290 ASSERT_NE(nullptr, text_range_provider.Get());
291
292 base::win::ScopedBstr text_content;
293 EXPECT_HRESULT_SUCCEEDED(
294 text_range_provider->GetText(-1, text_content.Receive()));
295 EXPECT_EQ(0, wcscmp(text_content.Get(), L"text child of text."));
296
297 ComPtr<IRawElementProviderSimple> enclosing_element;
298 text_range_provider->GetEnclosingElement(&enclosing_element);
299 EXPECT_EQ(text_child_of_text_text_provider_raw_.Get(),
300 enclosing_element.Get());
301 }
302
TEST_F(AXPlatformNodeTextChildProviderTest,ITextChildProviderTextRangeFromNontextChildOfNontext)303 TEST_F(AXPlatformNodeTextChildProviderTest,
304 ITextChildProviderTextRangeFromNontextChildOfNontext) {
305 ComPtr<ITextRangeProvider> text_range_provider;
306 EXPECT_HRESULT_SUCCEEDED(
307 nontext_child_of_nontext_text_child_provider_->get_TextRange(
308 &text_range_provider));
309 ASSERT_NE(nullptr, text_range_provider.Get());
310
311 base::win::ScopedBstr text_content;
312 EXPECT_HRESULT_SUCCEEDED(
313 text_range_provider->GetText(-1, text_content.Receive()));
314 EXPECT_EQ(0, wcscmp(text_content.Get(), kEmbeddedCharacterAsString.c_str()));
315
316 ComPtr<IRawElementProviderSimple> enclosing_element;
317 text_range_provider->GetEnclosingElement(&enclosing_element);
318 EXPECT_EQ(nontext_child_of_nontext_text_provider_raw_.Get(),
319 enclosing_element.Get());
320 }
321
TEST_F(AXPlatformNodeTextChildProviderTest,ITextChildProviderTextRangeFromTextChildOfNontext)322 TEST_F(AXPlatformNodeTextChildProviderTest,
323 ITextChildProviderTextRangeFromTextChildOfNontext) {
324 ComPtr<ITextRangeProvider> text_range_provider;
325 EXPECT_HRESULT_SUCCEEDED(
326 text_child_of_nontext_text_child_provider_->get_TextRange(
327 &text_range_provider));
328 ASSERT_NE(nullptr, text_range_provider.Get());
329
330 base::win::ScopedBstr text_content;
331 EXPECT_HRESULT_SUCCEEDED(
332 text_range_provider->GetText(-1, text_content.Receive()));
333 EXPECT_EQ(0, wcscmp(text_content.Get(), L"text child of nontext."));
334
335 ComPtr<IRawElementProviderSimple> enclosing_element;
336 text_range_provider->GetEnclosingElement(&enclosing_element);
337 EXPECT_EQ(text_child_of_nontext_text_provider_raw_.Get(),
338 enclosing_element.Get());
339 }
340
TEST_F(AXPlatformNodeTextChildProviderTest,ITextChildProviderTextRangeFromTextChildOfText)341 TEST_F(AXPlatformNodeTextChildProviderTest,
342 ITextChildProviderTextRangeFromTextChildOfText) {
343 ComPtr<ITextRangeProvider> text_range_provider;
344 EXPECT_HRESULT_SUCCEEDED(
345 text_child_of_text_text_child_provider_->get_TextRange(
346 &text_range_provider));
347 ASSERT_NE(nullptr, text_range_provider.Get());
348
349 base::win::ScopedBstr text_content;
350 EXPECT_HRESULT_SUCCEEDED(
351 text_range_provider->GetText(-1, text_content.Receive()));
352 EXPECT_EQ(0, wcscmp(text_content.Get(), L"text child of text."));
353
354 ComPtr<IRawElementProviderSimple> enclosing_element;
355 text_range_provider->GetEnclosingElement(&enclosing_element);
356 EXPECT_EQ(text_child_of_text_text_provider_raw_.Get(),
357 enclosing_element.Get());
358 }
359
360 // ITextChildProvider Tests - Inactive AX Tree
361 //
362 // Test that both ITextChildProvider::GetTextContainer and
363 // ITextChildProvider::GetTextContainer fail under an inactive AX tree.
TEST_F(AXPlatformNodeTextChildProviderTest,ITextChildProviderInactiveAccessibilityTree)364 TEST_F(AXPlatformNodeTextChildProviderTest,
365 ITextChildProviderInactiveAccessibilityTree) {
366 DestroyTree();
367
368 // Test that GetTextContainer fails under an inactive tree.
369 ComPtr<IRawElementProviderSimple> text_container;
370 HRESULT hr = nontext_child_of_root_text_child_provider_->get_TextContainer(
371 &text_container);
372 EXPECT_EQ(static_cast<HRESULT>(UIA_E_ELEMENTNOTAVAILABLE), hr);
373
374 // Test that GetTextRange fails under an inactive tree.
375 ComPtr<ITextRangeProvider> text_range_provider;
376 hr = nontext_child_of_root_text_child_provider_->get_TextRange(
377 &text_range_provider);
378 EXPECT_EQ(static_cast<HRESULT>(UIA_E_ELEMENTNOTAVAILABLE), hr);
379 }
380
381 } // namespace ui
382