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::kInlineTextBox;
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_root_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_root_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