1 // Copyright (c) 2012 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 "content/browser/accessibility/browser_accessibility_win.h"
6 
7 #include <string>
8 #include <vector>
9 
10 #include <objbase.h>
11 #include <stdint.h>
12 #include <wrl/client.h>
13 
14 #include <memory>
15 #include <utility>
16 
17 #include "base/command_line.h"
18 #include "base/macros.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/test/task_environment.h"
21 #include "base/win/scoped_bstr.h"
22 #include "base/win/scoped_variant.h"
23 #include "content/browser/accessibility/browser_accessibility_manager.h"
24 #include "content/browser/accessibility/browser_accessibility_manager_win.h"
25 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
26 #include "content/browser/accessibility/test_browser_accessibility_delegate.h"
27 #include "content/browser/renderer_host/legacy_render_widget_host_win.h"
28 #include "content/public/test/browser_task_environment.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30 #include "ui/accessibility/accessibility_switches.h"
31 #include "ui/accessibility/ax_node_data.h"
32 #include "ui/accessibility/platform/ax_platform_node_win.h"
33 #include "ui/base/win/atl_module.h"
34 
35 namespace content {
36 
37 #define EXPECT_IA2_TEXT_AT_OFFSET(provider, index, text_boundary, expected_hr, \
38                                   start, end, text)                            \
39   {                                                                            \
40     LONG actual_start;                                                         \
41     LONG actual_end;                                                           \
42     base::win::ScopedBstr actual_text;                                         \
43     EXPECT_EQ(expected_hr,                                                     \
44               provider->get_textAtOffset(index, text_boundary, &actual_start,  \
45                                          &actual_end, actual_text.Receive())); \
46     EXPECT_EQ(start, actual_start);                                            \
47     EXPECT_EQ(end, actual_end);                                                \
48     EXPECT_STREQ(text, actual_text.Get());                                     \
49   }
50 
51 #define EXPECT_IA2_TEXT_BEFORE_OFFSET(provider, index, text_boundary, \
52                                       expected_hr, start, end, text)  \
53   {                                                                   \
54     LONG actual_start;                                                \
55     LONG actual_end;                                                  \
56     base::win::ScopedBstr actual_text;                                \
57     EXPECT_EQ(expected_hr, provider->get_textBeforeOffset(            \
58                                index, text_boundary, &actual_start,   \
59                                &actual_end, actual_text.Receive()));  \
60     EXPECT_EQ(start, actual_start);                                   \
61     EXPECT_EQ(end, actual_end);                                       \
62     EXPECT_STREQ(text, actual_text.Get());                            \
63   }
64 
65 #define EXPECT_IA2_TEXT_AFTER_OFFSET(provider, index, text_boundary, \
66                                      expected_hr, start, end, text)  \
67   {                                                                  \
68     LONG actual_start;                                               \
69     LONG actual_end;                                                 \
70     base::win::ScopedBstr actual_text;                               \
71     EXPECT_EQ(expected_hr, provider->get_textAfterOffset(            \
72                                index, text_boundary, &actual_start,  \
73                                &actual_end, actual_text.Receive())); \
74     EXPECT_EQ(start, actual_start);                                  \
75     EXPECT_EQ(end, actual_end);                                      \
76     EXPECT_STREQ(text, actual_text.Get());                           \
77   }
78 
79 #define EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants,                 \
80                                                 expected_descendants)        \
81   {                                                                          \
82     size_t count = descendants.size();                                       \
83     EXPECT_EQ(count, expected_descendants.size());                           \
84     for (size_t i = 0; i < count; ++i) {                                     \
85       EXPECT_EQ(ui::AXPlatformNode::FromNativeViewAccessible(descendants[i]) \
86                     ->GetDelegate()                                          \
87                     ->GetData()                                              \
88                     .ToString(),                                             \
89                 ui::AXPlatformNode::FromNativeViewAccessible(                \
90                     expected_descendants[i])                                 \
91                     ->GetDelegate()                                          \
92                     ->GetData()                                              \
93                     .ToString());                                            \
94     }                                                                        \
95   }
96 
97 // BrowserAccessibilityWinTest ------------------------------------------------
98 
99 class BrowserAccessibilityWinTest : public testing::Test {
100  public:
101   BrowserAccessibilityWinTest();
102   ~BrowserAccessibilityWinTest() override;
103 
104  protected:
105   std::unique_ptr<TestBrowserAccessibilityDelegate>
106       test_browser_accessibility_delegate_;
107 
108  private:
109   void SetUp() override;
110 
111   content::BrowserTaskEnvironment task_environment_;
112 
113   DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityWinTest);
114 };
115 
BrowserAccessibilityWinTest()116 BrowserAccessibilityWinTest::BrowserAccessibilityWinTest() {}
117 
~BrowserAccessibilityWinTest()118 BrowserAccessibilityWinTest::~BrowserAccessibilityWinTest() {}
119 
SetUp()120 void BrowserAccessibilityWinTest::SetUp() {
121   ui::win::CreateATLModuleIfNeeded();
122   test_browser_accessibility_delegate_ =
123       std::make_unique<TestBrowserAccessibilityDelegate>();
124 }
125 
126 // Actual tests ---------------------------------------------------------------
127 
128 // Test that BrowserAccessibilityManager correctly releases the tree of
129 // BrowserAccessibility instances upon delete.
TEST_F(BrowserAccessibilityWinTest,TestNoLeaks)130 TEST_F(BrowserAccessibilityWinTest, TestNoLeaks) {
131   // Create ui::AXNodeData objects for a simple document tree,
132   // representing the accessibility information used to initialize
133   // BrowserAccessibilityManager.
134   ui::AXNodeData button;
135   button.id = 2;
136   button.role = ax::mojom::Role::kButton;
137   button.SetName("Button");
138 
139   ui::AXNodeData checkbox;
140   checkbox.id = 3;
141   checkbox.role = ax::mojom::Role::kCheckBox;
142   checkbox.SetName("Checkbox");
143 
144   ui::AXNodeData root;
145   root.id = 1;
146   root.role = ax::mojom::Role::kRootWebArea;
147   root.SetName("Document");
148   root.child_ids.push_back(2);
149   root.child_ids.push_back(3);
150 
151   // Construct a BrowserAccessibilityManager with this
152   // ui::AXNodeData tree and a factory for an instance-counting
153   // BrowserAccessibility, and ensure that exactly 3 instances were
154   // created. Note that the manager takes ownership of the factory.
155   std::unique_ptr<BrowserAccessibilityManager> manager(
156       BrowserAccessibilityManager::Create(
157           MakeAXTreeUpdate(root, button, checkbox),
158           test_browser_accessibility_delegate_.get()));
159 
160   // Delete the manager and test that all 3 instances are deleted.
161   manager.reset();
162 
163   // Construct a manager again, and this time use the IAccessible interface
164   // to get new references to two of the three nodes in the tree.
165   manager.reset(BrowserAccessibilityManager::Create(
166       MakeAXTreeUpdate(root, button, checkbox),
167       test_browser_accessibility_delegate_.get()));
168   IAccessible* root_accessible =
169       ToBrowserAccessibilityWin(manager->GetRoot())->GetCOM();
170   IDispatch* root_iaccessible = NULL;
171   IDispatch* child1_iaccessible = NULL;
172   base::win::ScopedVariant childid_self(CHILDID_SELF);
173   HRESULT hr = root_accessible->get_accChild(childid_self, &root_iaccessible);
174   ASSERT_EQ(S_OK, hr);
175   base::win::ScopedVariant one(1);
176   hr = root_accessible->get_accChild(one, &child1_iaccessible);
177   ASSERT_EQ(S_OK, hr);
178 
179   // Now delete the manager, and only one of the three nodes in the tree
180   // should be released.
181   manager.reset();
182 
183   // Release each of our references and make sure that each one results in
184   // the instance being deleted as its reference count hits zero.
185   root_iaccessible->Release();
186   child1_iaccessible->Release();
187 }
188 
TEST_F(BrowserAccessibilityWinTest,TestChildrenChange)189 TEST_F(BrowserAccessibilityWinTest, TestChildrenChange) {
190   // Create ui::AXNodeData objects for a simple document tree,
191   // representing the accessibility information used to initialize
192   // BrowserAccessibilityManager.
193   ui::AXNodeData text;
194   text.id = 2;
195   text.role = ax::mojom::Role::kStaticText;
196   text.SetName("old text");
197 
198   ui::AXNodeData root;
199   root.id = 1;
200   root.role = ax::mojom::Role::kRootWebArea;
201   root.SetName("Document");
202   root.child_ids.push_back(2);
203 
204   // Construct a BrowserAccessibilityManager with this
205   // ui::AXNodeData tree and a factory for an instance-counting
206   // BrowserAccessibility.
207   std::unique_ptr<BrowserAccessibilityManager> manager(
208       BrowserAccessibilityManager::Create(
209           MakeAXTreeUpdate(root, text),
210           test_browser_accessibility_delegate_.get()));
211 
212   // Query for the text IAccessible and verify that it returns "old text" as its
213   // value.
214   base::win::ScopedVariant one(1);
215   Microsoft::WRL::ComPtr<IDispatch> text_dispatch;
216   HRESULT hr = ToBrowserAccessibilityWin(manager->GetRoot())
217                    ->GetCOM()
218                    ->get_accChild(one, &text_dispatch);
219   ASSERT_EQ(S_OK, hr);
220 
221   Microsoft::WRL::ComPtr<IAccessible> text_accessible;
222   hr = text_dispatch.As(&text_accessible);
223   ASSERT_EQ(S_OK, hr);
224 
225   base::win::ScopedVariant childid_self(CHILDID_SELF);
226   base::win::ScopedBstr name;
227   hr = text_accessible->get_accName(childid_self, name.Receive());
228   ASSERT_EQ(S_OK, hr);
229   EXPECT_EQ(L"old text", base::string16(name.Get()));
230   name.Reset();
231 
232   text_dispatch.Reset();
233   text_accessible.Reset();
234 
235   // Notify the BrowserAccessibilityManager that the text child has changed.
236   ui::AXNodeData text2;
237   text2.id = 2;
238   text2.role = ax::mojom::Role::kStaticText;
239   text2.SetName("new text");
240   AXEventNotificationDetails event_bundle;
241   event_bundle.updates.resize(1);
242   event_bundle.updates[0].nodes.push_back(text2);
243   ASSERT_TRUE(manager->OnAccessibilityEvents(event_bundle));
244 
245   // Query for the text IAccessible and verify that it now returns "new text"
246   // as its value.
247   hr = ToBrowserAccessibilityWin(manager->GetRoot())
248            ->GetCOM()
249            ->get_accChild(one, &text_dispatch);
250   ASSERT_EQ(S_OK, hr);
251 
252   hr = text_dispatch.As(&text_accessible);
253   ASSERT_EQ(S_OK, hr);
254 
255   hr = text_accessible->get_accName(childid_self, name.Receive());
256   ASSERT_EQ(S_OK, hr);
257   EXPECT_EQ(L"new text", base::string16(name.Get()));
258 
259   text_dispatch.Reset();
260   text_accessible.Reset();
261 
262   // Delete the manager and test that all BrowserAccessibility instances are
263   // deleted.
264   manager.reset();
265 }
266 
TEST_F(BrowserAccessibilityWinTest,TestChildrenChangeNoLeaks)267 TEST_F(BrowserAccessibilityWinTest, TestChildrenChangeNoLeaks) {
268   // Create ui::AXNodeData objects for a simple document tree,
269   // representing the accessibility information used to initialize
270   // BrowserAccessibilityManager.
271   ui::AXNodeData div;
272   div.id = 2;
273   div.role = ax::mojom::Role::kGroup;
274 
275   ui::AXNodeData text3;
276   text3.id = 3;
277   text3.role = ax::mojom::Role::kStaticText;
278 
279   ui::AXNodeData text4;
280   text4.id = 4;
281   text4.role = ax::mojom::Role::kStaticText;
282 
283   div.child_ids.push_back(3);
284   div.child_ids.push_back(4);
285 
286   ui::AXNodeData root;
287   root.id = 1;
288   root.role = ax::mojom::Role::kRootWebArea;
289   root.child_ids.push_back(2);
290 
291   // Construct a BrowserAccessibilityManager with this
292   // ui::AXNodeData tree and a factory for an instance-counting
293   // BrowserAccessibility and ensure that exactly 4 instances were
294   // created. Note that the manager takes ownership of the factory.
295   std::unique_ptr<BrowserAccessibilityManager> manager(
296       BrowserAccessibilityManager::Create(
297           MakeAXTreeUpdate(root, div, text3, text4),
298           test_browser_accessibility_delegate_.get()));
299 
300   // Notify the BrowserAccessibilityManager that the div node and its children
301   // were removed and ensure that only one BrowserAccessibility instance exists.
302   root.child_ids.clear();
303   AXEventNotificationDetails event_bundle;
304   event_bundle.updates.resize(1);
305   event_bundle.updates[0].nodes.push_back(root);
306   ASSERT_TRUE(manager->OnAccessibilityEvents(event_bundle));
307 
308   // Delete the manager and test that all BrowserAccessibility instances are
309   // deleted.
310   manager.reset();
311 }
312 
TEST_F(BrowserAccessibilityWinTest,TestTextBoundaries)313 TEST_F(BrowserAccessibilityWinTest, TestTextBoundaries) {
314   //
315   // +-1 root
316   //   +-2 text_field
317   //     +-3 static_text1 "One two three."
318   //     | +-4 inline_box1 "One two three."
319   //     +-5 line_break1 "\n"
320   //     +-6 static_text2 "Four five six."
321   //     | +-7 inline_box2 "Four five six."
322   //     +-8 line_break2 "\n" kIsLineBreakingObject
323   //     +-9 static_text3 "Seven eight nine."
324   //       +-10 inline_box3 "Seven eight nine."
325   //
326   std::string line1 = "One two three.";
327   std::string line2 = "Four five six.";
328   std::string line3 = "Seven eight nine.";
329   std::string text_value = line1 + '\n' + line2 + '\n' + line3;
330 
331   ui::AXNodeData root;
332   root.id = 1;
333   root.role = ax::mojom::Role::kRootWebArea;
334   root.child_ids.push_back(2);
335 
336   ui::AXNodeData text_field;
337   text_field.id = 2;
338   text_field.role = ax::mojom::Role::kTextField;
339   text_field.AddState(ax::mojom::State::kEditable);
340   text_field.SetValue(text_value);
341   text_field.AddIntListAttribute(ax::mojom::IntListAttribute::kCachedLineStarts,
342                                  {15});
343   text_field.child_ids.push_back(3);
344   text_field.child_ids.push_back(5);
345   text_field.child_ids.push_back(6);
346   text_field.child_ids.push_back(8);
347   text_field.child_ids.push_back(9);
348 
349   ui::AXNodeData static_text1;
350   static_text1.id = 3;
351   static_text1.role = ax::mojom::Role::kStaticText;
352   static_text1.AddState(ax::mojom::State::kEditable);
353   static_text1.SetName(line1);
354   static_text1.child_ids.push_back(4);
355 
356   ui::AXNodeData inline_box1;
357   inline_box1.id = 4;
358   inline_box1.role = ax::mojom::Role::kInlineTextBox;
359   inline_box1.AddState(ax::mojom::State::kEditable);
360   inline_box1.SetName(line1);
361   inline_box1.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
362                                   {0, 4, 8});
363 
364   ui::AXNodeData line_break1;
365   line_break1.id = 5;
366   line_break1.role = ax::mojom::Role::kLineBreak;
367   line_break1.AddState(ax::mojom::State::kEditable);
368   line_break1.SetName("\n");
369 
370   inline_box1.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
371                               line_break1.id);
372   line_break1.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
373                               inline_box1.id);
374 
375   ui::AXNodeData static_text2;
376   static_text2.id = 6;
377   static_text2.role = ax::mojom::Role::kStaticText;
378   static_text2.AddState(ax::mojom::State::kEditable);
379   static_text2.SetName(line2);
380   static_text2.child_ids.push_back(7);
381 
382   ui::AXNodeData inline_box2;
383   inline_box2.id = 7;
384   inline_box2.role = ax::mojom::Role::kInlineTextBox;
385   inline_box2.AddState(ax::mojom::State::kEditable);
386   inline_box2.SetName(line2);
387   inline_box2.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
388                                   {0, 5, 10});
389 
390   ui::AXNodeData line_break2;
391   line_break2.id = 8;
392   line_break2.role = ax::mojom::Role::kLineBreak;
393   line_break2.AddState(ax::mojom::State::kEditable);
394   line_break2.SetName("\n");
395   line_break2.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
396                                true);
397 
398   inline_box2.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
399                               line_break2.id);
400   line_break2.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
401                               inline_box2.id);
402 
403   ui::AXNodeData static_text3;
404   static_text3.id = 9;
405   static_text3.role = ax::mojom::Role::kStaticText;
406   static_text3.AddState(ax::mojom::State::kEditable);
407   static_text3.SetName(line3);
408   static_text3.child_ids.push_back(10);
409 
410   ui::AXNodeData inline_box3;
411   inline_box3.id = 10;
412   inline_box3.role = ax::mojom::Role::kInlineTextBox;
413   inline_box3.AddState(ax::mojom::State::kEditable);
414   inline_box3.SetName(line3);
415   inline_box3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
416                                   {0, 6, 12});
417 
418   std::unique_ptr<BrowserAccessibilityManager> manager(
419       BrowserAccessibilityManager::Create(
420           MakeAXTreeUpdate(root, text_field, static_text1, inline_box1,
421                            line_break1, static_text2, inline_box2, line_break2,
422                            static_text3, inline_box3),
423           test_browser_accessibility_delegate_.get()));
424 
425   BrowserAccessibilityWin* root_obj =
426       ToBrowserAccessibilityWin(manager->GetRoot());
427   ASSERT_NE(nullptr, root_obj);
428   ASSERT_EQ(1U, root_obj->PlatformChildCount());
429 
430   BrowserAccessibilityComWin* text_field_obj =
431       ToBrowserAccessibilityComWin(root_obj->PlatformGetChild(0));
432   ASSERT_NE(nullptr, text_field_obj);
433 
434   LONG text_len;
435   EXPECT_EQ(S_OK, text_field_obj->get_nCharacters(&text_len));
436 
437   base::win::ScopedBstr text;
438   EXPECT_EQ(S_OK, text_field_obj->get_text(0, text_len, text.Receive()));
439   EXPECT_EQ(text_value, base::UTF16ToUTF8(base::string16(text.Get())));
440   text.Reset();
441 
442   EXPECT_EQ(S_OK, text_field_obj->get_text(0, 4, text.Receive()));
443   EXPECT_STREQ(L"One ", text.Get());
444   text.Reset();
445 
446   EXPECT_IA2_TEXT_AT_OFFSET(text_field_obj, 1, IA2_TEXT_BOUNDARY_CHAR,
447                             /*expected_hr=*/S_OK, /*start=*/1, /*end=*/2,
448                             /*text=*/L"n");
449 
450   EXPECT_IA2_TEXT_AT_OFFSET(text_field_obj, text_len, IA2_TEXT_BOUNDARY_CHAR,
451                             /*expected_hr=*/E_INVALIDARG, /*start=*/0,
452                             /*end=*/0,
453                             /*text=*/nullptr);
454 
455   EXPECT_IA2_TEXT_AT_OFFSET(text_field_obj, text_len, IA2_TEXT_BOUNDARY_WORD,
456                             /*expected_hr=*/E_INVALIDARG, /*start=*/0,
457                             /*end=*/0,
458                             /*text=*/nullptr);
459 
460   EXPECT_IA2_TEXT_AT_OFFSET(text_field_obj, 1, IA2_TEXT_BOUNDARY_WORD,
461                             /*expected_hr=*/S_OK, /*start=*/0, /*end=*/4,
462                             /*text=*/L"One ");
463 
464   EXPECT_IA2_TEXT_AT_OFFSET(text_field_obj, 6, IA2_TEXT_BOUNDARY_WORD,
465                             /*expected_hr=*/S_OK, /*start=*/4, /*end=*/8,
466                             /*text=*/L"two ");
467 
468   EXPECT_IA2_TEXT_AT_OFFSET(text_field_obj, text_len - 1,
469                             IA2_TEXT_BOUNDARY_WORD,
470                             /*expected_hr=*/S_OK, /*start=*/42, /*end=*/47,
471                             /*text=*/L"nine.");
472 
473   EXPECT_IA2_TEXT_AT_OFFSET(text_field_obj, 1, IA2_TEXT_BOUNDARY_LINE,
474                             /*expected_hr=*/S_OK, /*start=*/0, /*end=*/15,
475                             /*text=*/L"One two three.\n");
476 
477   EXPECT_IA2_TEXT_AT_OFFSET(text_field_obj, text_len, IA2_TEXT_BOUNDARY_LINE,
478                             /*expected_hr=*/S_OK, /*start=*/30, /*end=*/47,
479                             /*text=*/L"Seven eight nine.");
480 
481   EXPECT_IA2_TEXT_AT_OFFSET(text_field_obj, 1, IA2_TEXT_BOUNDARY_PARAGRAPH,
482                             /*expected_hr=*/S_OK, /*start=*/0, /*end=*/30,
483                             /*text=*/L"One two three.\nFour five six.\n");
484 
485   EXPECT_IA2_TEXT_AT_OFFSET(text_field_obj, text_len - 1,
486                             IA2_TEXT_BOUNDARY_PARAGRAPH,
487                             /*expected_hr=*/S_OK, /*start=*/30, /*end=*/47,
488                             /*text=*/L"Seven eight nine.");
489 
490   EXPECT_IA2_TEXT_AT_OFFSET(text_field_obj, text_len,
491                             IA2_TEXT_BOUNDARY_PARAGRAPH,
492                             /*expected_hr=*/E_INVALIDARG, /*start=*/0,
493                             /*end=*/0,
494                             /*text=*/nullptr);
495 
496   EXPECT_IA2_TEXT_BEFORE_OFFSET(text_field_obj, 0, IA2_TEXT_BOUNDARY_CHAR,
497                                 /*expected_hr=*/S_FALSE, /*start=*/0, /*end=*/0,
498                                 /*text=*/nullptr);
499 
500   EXPECT_IA2_TEXT_BEFORE_OFFSET(text_field_obj, 1, IA2_TEXT_BOUNDARY_CHAR,
501                                 /*expected_hr=*/S_OK, /*start=*/0, /*end=*/1,
502                                 /*text=*/L"O");
503 
504   EXPECT_IA2_TEXT_BEFORE_OFFSET(
505       text_field_obj, text_len, IA2_TEXT_BOUNDARY_CHAR,
506       /*expected_hr=*/E_INVALIDARG, /*start=*/0, /*end=*/0,
507       /*text=*/nullptr);
508 
509   EXPECT_IA2_TEXT_BEFORE_OFFSET(
510       text_field_obj, text_len, IA2_TEXT_BOUNDARY_WORD,
511       /*expected_hr=*/E_INVALIDARG, /*start=*/0, /*end=*/0,
512       /*text=*/nullptr);
513 
514   EXPECT_IA2_TEXT_BEFORE_OFFSET(text_field_obj, 1, IA2_TEXT_BOUNDARY_WORD,
515                                 /*expected_hr=*/S_FALSE, /*start=*/0, /*end=*/0,
516                                 /*text=*/nullptr);
517 
518   EXPECT_IA2_TEXT_BEFORE_OFFSET(text_field_obj, 4, IA2_TEXT_BOUNDARY_WORD,
519                                 /*expected_hr=*/S_OK, /*start=*/0, /*end=*/4,
520                                 /*text=*/L"One ");
521 
522   EXPECT_IA2_TEXT_BEFORE_OFFSET(text_field_obj, 6, IA2_TEXT_BOUNDARY_WORD,
523                                 /*expected_hr=*/S_OK, /*start=*/0, /*end=*/4,
524                                 /*text=*/L"One ");
525 
526   EXPECT_IA2_TEXT_BEFORE_OFFSET(text_field_obj, text_len - 1,
527                                 IA2_TEXT_BOUNDARY_WORD,
528                                 /*expected_hr=*/S_OK, /*start=*/36, /*end=*/42,
529                                 /*text=*/L"eight ");
530 
531   EXPECT_IA2_TEXT_BEFORE_OFFSET(text_field_obj, 0, IA2_TEXT_BOUNDARY_LINE,
532                                 /*expected_hr=*/S_FALSE, /*start=*/0, /*end=*/0,
533                                 /*text=*/nullptr);
534 
535   EXPECT_IA2_TEXT_BEFORE_OFFSET(text_field_obj, text_len - 1,
536                                 IA2_TEXT_BOUNDARY_LINE,
537                                 /*expected_hr=*/S_OK, /*start=*/15, /*end=*/30,
538                                 /*text=*/L"Four five six.\n");
539 
540   EXPECT_IA2_TEXT_BEFORE_OFFSET(text_field_obj, 18, IA2_TEXT_BOUNDARY_PARAGRAPH,
541                                 /*expected_hr=*/S_FALSE, /*start=*/0, /*end=*/0,
542                                 /*text=*/nullptr);
543 
544   EXPECT_IA2_TEXT_BEFORE_OFFSET(text_field_obj, text_len - 1,
545                                 IA2_TEXT_BOUNDARY_PARAGRAPH,
546                                 /*expected_hr=*/S_OK, /*start=*/0, /*end=*/30,
547                                 /*text=*/L"One two three.\nFour five six.\n");
548 
549   EXPECT_IA2_TEXT_AFTER_OFFSET(text_field_obj, 0, IA2_TEXT_BOUNDARY_CHAR,
550                                /*expected_hr=*/S_OK, /*start=*/1, /*end=*/2,
551                                /*text=*/L"n");
552 
553   EXPECT_IA2_TEXT_AFTER_OFFSET(text_field_obj, 1, IA2_TEXT_BOUNDARY_CHAR,
554                                /*expected_hr=*/S_OK, /*start=*/2, /*end=*/3,
555                                /*text=*/L"e");
556 
557   EXPECT_IA2_TEXT_AFTER_OFFSET(text_field_obj, text_len, IA2_TEXT_BOUNDARY_CHAR,
558                                /*expected_hr=*/E_INVALIDARG, /*start=*/0,
559                                /*end=*/0,
560                                /*text=*/nullptr);
561 
562   EXPECT_IA2_TEXT_AFTER_OFFSET(text_field_obj, 1, IA2_TEXT_BOUNDARY_WORD,
563                                /*expected_hr=*/S_OK, /*start=*/4, /*end=*/8,
564                                /*text=*/L"two ");
565 
566   EXPECT_IA2_TEXT_AFTER_OFFSET(text_field_obj, 6, IA2_TEXT_BOUNDARY_WORD,
567                                /*expected_hr=*/S_OK, /*start=*/8, /*end=*/15,
568                                /*text=*/L"three.\n");
569 
570   EXPECT_IA2_TEXT_AFTER_OFFSET(text_field_obj, text_len, IA2_TEXT_BOUNDARY_WORD,
571                                /*expected_hr=*/E_INVALIDARG, /*start=*/0,
572                                /*end=*/0,
573                                /*text=*/nullptr);
574 
575   EXPECT_IA2_TEXT_AFTER_OFFSET(text_field_obj, 0, IA2_TEXT_BOUNDARY_LINE,
576                                /*expected_hr=*/S_OK, /*start=*/15, /*end=*/30,
577                                /*text=*/L"Four five six.\n");
578 
579   EXPECT_IA2_TEXT_AFTER_OFFSET(text_field_obj, text_len - 1,
580                                IA2_TEXT_BOUNDARY_LINE,
581                                /*expected_hr=*/S_FALSE, /*start=*/0, /*end=*/0,
582                                /*text=*/nullptr);
583 
584   EXPECT_IA2_TEXT_AFTER_OFFSET(text_field_obj, 18, IA2_TEXT_BOUNDARY_PARAGRAPH,
585                                /*expected_hr=*/S_OK, /*start=*/30, /*end=*/47,
586                                /*text=*/L"Seven eight nine.");
587 
588   EXPECT_IA2_TEXT_AFTER_OFFSET(text_field_obj, text_len - 1,
589                                IA2_TEXT_BOUNDARY_PARAGRAPH,
590                                /*expected_hr=*/S_FALSE, /*start=*/0, /*end=*/0,
591                                /*text=*/nullptr);
592 
593   EXPECT_EQ(S_OK, text_field_obj->get_text(0, IA2_TEXT_OFFSET_LENGTH,
594                                            text.Receive()));
595   EXPECT_EQ(text_value, base::UTF16ToUTF8(base::string16(text.Get())));
596 
597   // Delete the manager and test that all BrowserAccessibility instances are
598   // deleted.
599   manager.reset();
600 }
601 
TEST_F(BrowserAccessibilityWinTest,TestSimpleHypertext)602 TEST_F(BrowserAccessibilityWinTest, TestSimpleHypertext) {
603   const std::string text1_name = "One two three.";
604   const std::string text2_name = " Four five six.";
605   const LONG text_name_len = text1_name.length() + text2_name.length();
606 
607   ui::AXNodeData text1;
608   text1.id = 11;
609   text1.role = ax::mojom::Role::kStaticText;
610   text1.SetName(text1_name);
611 
612   ui::AXNodeData text2;
613   text2.id = 12;
614   text2.role = ax::mojom::Role::kStaticText;
615   text2.SetName(text2_name);
616 
617   ui::AXNodeData root;
618   root.id = 1;
619   root.role = ax::mojom::Role::kRootWebArea;
620   root.child_ids.push_back(text1.id);
621   root.child_ids.push_back(text2.id);
622 
623   std::unique_ptr<BrowserAccessibilityManager> manager(
624       BrowserAccessibilityManager::Create(
625           MakeAXTreeUpdate(root, text1, text2),
626           test_browser_accessibility_delegate_.get()));
627 
628   BrowserAccessibilityComWin* root_obj =
629       ToBrowserAccessibilityWin(manager->GetRoot())->GetCOM();
630 
631   LONG text_len;
632   EXPECT_EQ(S_OK, root_obj->get_nCharacters(&text_len));
633   EXPECT_EQ(text_name_len, text_len);
634 
635   base::win::ScopedBstr text;
636   EXPECT_EQ(S_OK, root_obj->get_text(0, text_name_len, text.Receive()));
637   EXPECT_EQ(text1_name + text2_name,
638             base::UTF16ToUTF8(base::string16(text.Get())));
639 
640   LONG hyperlink_count;
641   EXPECT_EQ(S_OK, root_obj->get_nHyperlinks(&hyperlink_count));
642   EXPECT_EQ(0, hyperlink_count);
643 
644   Microsoft::WRL::ComPtr<IAccessibleHyperlink> hyperlink;
645   EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(-1, &hyperlink));
646   EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(0, &hyperlink));
647   EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(text_name_len, &hyperlink));
648   EXPECT_EQ(E_INVALIDARG,
649             root_obj->get_hyperlink(text_name_len + 1, &hyperlink));
650 
651   LONG hyperlink_index;
652   EXPECT_EQ(S_FALSE, root_obj->get_hyperlinkIndex(0, &hyperlink_index));
653   EXPECT_EQ(-1, hyperlink_index);
654   // Invalid arguments should not be modified.
655   hyperlink_index = -2;
656   EXPECT_EQ(E_INVALIDARG,
657             root_obj->get_hyperlinkIndex(text_name_len, &hyperlink_index));
658   EXPECT_EQ(-2, hyperlink_index);
659   EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlinkIndex(-1, &hyperlink_index));
660   EXPECT_EQ(-2, hyperlink_index);
661   EXPECT_EQ(E_INVALIDARG,
662             root_obj->get_hyperlinkIndex(text_name_len + 1, &hyperlink_index));
663   EXPECT_EQ(-2, hyperlink_index);
664 
665   manager.reset();
666 }
667 
TEST_F(BrowserAccessibilityWinTest,TestComplexHypertext)668 TEST_F(BrowserAccessibilityWinTest, TestComplexHypertext) {
669   const base::string16 text1_name = L"One two three.";
670   const base::string16 combo_box_name = L"City:";
671   const base::string16 combo_box_value = L"Happyland";
672   const base::string16 text2_name = L" Four five six.";
673   const base::string16 check_box_name = L"I agree";
674   const base::string16 check_box_value = L"Checked";
675   const base::string16 button_text_name = L"Red";
676   const base::string16 link_text_name = L"Blue";
677   // Each control (combo / check box, button and link) will be represented by an
678   // embedded object character.
679   const base::string16 embed(1, BrowserAccessibilityComWin::kEmbeddedCharacter);
680   const base::string16 root_hypertext =
681       text1_name + embed + text2_name + embed + embed + embed;
682   const LONG root_hypertext_len = root_hypertext.length();
683 
684   ui::AXNodeData text1;
685   text1.id = 11;
686   text1.role = ax::mojom::Role::kStaticText;
687   text1.SetName(base::UTF16ToUTF8(text1_name));
688 
689   ui::AXNodeData combo_box;
690   combo_box.id = 12;
691   combo_box.role = ax::mojom::Role::kTextFieldWithComboBox;
692   combo_box.AddState(ax::mojom::State::kEditable);
693   combo_box.SetName(base::UTF16ToUTF8(combo_box_name));
694   combo_box.SetValue(base::UTF16ToUTF8(combo_box_value));
695 
696   ui::AXNodeData text2;
697   text2.id = 13;
698   text2.role = ax::mojom::Role::kStaticText;
699   text2.SetName(base::UTF16ToUTF8(text2_name));
700 
701   ui::AXNodeData check_box;
702   check_box.id = 14;
703   check_box.role = ax::mojom::Role::kCheckBox;
704   check_box.SetCheckedState(ax::mojom::CheckedState::kTrue);
705   check_box.SetName(base::UTF16ToUTF8(check_box_name));
706   // ARIA checkbox where the name is derived from its inner text.
707   check_box.SetNameFrom(ax::mojom::NameFrom::kContents);
708   check_box.SetValue(base::UTF16ToUTF8(check_box_value));
709 
710   ui::AXNodeData button, button_text;
711   button.id = 15;
712   button_text.id = 17;
713   button.role = ax::mojom::Role::kButton;
714   button.SetName(base::UTF16ToUTF8(button_text_name));
715   button.SetNameFrom(ax::mojom::NameFrom::kContents);
716   // A single text child with the same name should be hidden from accessibility
717   // to prevent double speaking.
718   button_text.role = ax::mojom::Role::kStaticText;
719   button_text.SetName(base::UTF16ToUTF8(button_text_name));
720   button.child_ids.push_back(button_text.id);
721 
722   ui::AXNodeData link, link_text;
723   link.id = 16;
724   link_text.id = 18;
725   link.role = ax::mojom::Role::kLink;
726   link_text.role = ax::mojom::Role::kStaticText;
727   link_text.SetName(base::UTF16ToUTF8(link_text_name));
728   link.child_ids.push_back(link_text.id);
729 
730   ui::AXNodeData root;
731   root.id = 1;
732   root.role = ax::mojom::Role::kRootWebArea;
733   root.child_ids.push_back(text1.id);
734   root.child_ids.push_back(combo_box.id);
735   root.child_ids.push_back(text2.id);
736   root.child_ids.push_back(check_box.id);
737   root.child_ids.push_back(button.id);
738   root.child_ids.push_back(link.id);
739 
740   std::unique_ptr<BrowserAccessibilityManager> manager(
741       BrowserAccessibilityManager::Create(
742           MakeAXTreeUpdate(root, text1, combo_box, text2, check_box, button,
743                            button_text, link, link_text),
744           test_browser_accessibility_delegate_.get()));
745 
746   BrowserAccessibilityComWin* root_obj =
747       ToBrowserAccessibilityWin(manager->GetRoot())->GetCOM();
748 
749   LONG text_len;
750   EXPECT_EQ(S_OK, root_obj->get_nCharacters(&text_len));
751   EXPECT_EQ(root_hypertext_len, text_len);
752 
753   base::win::ScopedBstr text;
754   EXPECT_EQ(S_OK, root_obj->get_text(0, root_hypertext_len, text.Receive()));
755   EXPECT_STREQ(root_hypertext.c_str(), text.Get());
756   text.Reset();
757 
758   LONG hyperlink_count;
759   EXPECT_EQ(S_OK, root_obj->get_nHyperlinks(&hyperlink_count));
760   EXPECT_EQ(4, hyperlink_count);
761 
762   Microsoft::WRL::ComPtr<IAccessibleHyperlink> hyperlink;
763   Microsoft::WRL::ComPtr<IAccessibleText> hypertext;
764   EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(-1, &hyperlink));
765   EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(4, &hyperlink));
766 
767   // Get the text of the combo box.
768   // It should be its value.
769   EXPECT_EQ(S_OK, root_obj->get_hyperlink(0, &hyperlink));
770   EXPECT_EQ(S_OK, hyperlink.As(&hypertext));
771   EXPECT_EQ(S_OK,
772             hypertext->get_text(0, IA2_TEXT_OFFSET_LENGTH, text.Receive()));
773   EXPECT_STREQ(combo_box_value.c_str(), text.Get());
774   text.Reset();
775   hyperlink.Reset();
776   hypertext.Reset();
777 
778   // Get the text of the check box.
779   // It should be its name.
780   EXPECT_EQ(S_OK, root_obj->get_hyperlink(1, &hyperlink));
781   EXPECT_EQ(S_OK, hyperlink.As(&hypertext));
782   EXPECT_EQ(S_OK,
783             hypertext->get_text(0, IA2_TEXT_OFFSET_LENGTH, text.Receive()));
784   EXPECT_STREQ(check_box_name.c_str(), text.Get());
785   text.Reset();
786   hyperlink.Reset();
787   hypertext.Reset();
788 
789   // Get the text of the button.
790   EXPECT_EQ(S_OK, root_obj->get_hyperlink(2, &hyperlink));
791   EXPECT_EQ(S_OK, hyperlink.As(&hypertext));
792   EXPECT_EQ(S_OK,
793             hypertext->get_text(0, IA2_TEXT_OFFSET_LENGTH, text.Receive()));
794   EXPECT_STREQ(button_text_name.c_str(), text.Get());
795   text.Reset();
796   hyperlink.Reset();
797   hypertext.Reset();
798 
799   // Get the text of the link.
800   EXPECT_EQ(S_OK, root_obj->get_hyperlink(3, &hyperlink));
801   EXPECT_EQ(S_OK, hyperlink.As(&hypertext));
802   EXPECT_EQ(S_OK, hypertext->get_text(0, 4, text.Receive()));
803   EXPECT_STREQ(link_text_name.c_str(), text.Get());
804   text.Reset();
805   hyperlink.Reset();
806   hypertext.Reset();
807 
808   LONG hyperlink_index;
809   EXPECT_EQ(S_FALSE, root_obj->get_hyperlinkIndex(0, &hyperlink_index));
810   EXPECT_EQ(-1, hyperlink_index);
811   // Invalid arguments should not be modified.
812   hyperlink_index = -2;
813   EXPECT_EQ(E_INVALIDARG,
814             root_obj->get_hyperlinkIndex(root_hypertext_len, &hyperlink_index));
815   EXPECT_EQ(-2, hyperlink_index);
816   EXPECT_EQ(S_OK, root_obj->get_hyperlinkIndex(14, &hyperlink_index));
817   EXPECT_EQ(0, hyperlink_index);
818   EXPECT_EQ(S_OK, root_obj->get_hyperlinkIndex(30, &hyperlink_index));
819   EXPECT_EQ(1, hyperlink_index);
820   EXPECT_EQ(S_OK, root_obj->get_hyperlinkIndex(31, &hyperlink_index));
821   EXPECT_EQ(2, hyperlink_index);
822   EXPECT_EQ(S_OK, root_obj->get_hyperlinkIndex(32, &hyperlink_index));
823   EXPECT_EQ(3, hyperlink_index);
824 
825   manager.reset();
826 }
827 
TEST_F(BrowserAccessibilityWinTest,TestGetUIADescendants)828 TEST_F(BrowserAccessibilityWinTest, TestGetUIADescendants) {
829   // Set up ax tree with the following structure:
830   //
831   // root___________________________________________________
832   // |               |       |                              |
833   // para1____       text3   para2____ (hidden)             button
834   // |       |               |       |                      |
835   // text1   text2           text4   text5 (visible)        image
836   ui::AXNodeData text1;
837   text1.id = 111;
838   text1.role = ax::mojom::Role::kStaticText;
839   text1.SetName("One two three.");
840 
841   ui::AXNodeData text2;
842   text2.id = 112;
843   text2.role = ax::mojom::Role::kStaticText;
844   text2.SetName("Two three four.");
845 
846   ui::AXNodeData text3;
847   text3.id = 113;
848   text3.role = ax::mojom::Role::kStaticText;
849   text3.SetName("Three four five.");
850 
851   ui::AXNodeData text4;
852   text4.id = 114;
853   text4.role = ax::mojom::Role::kStaticText;
854   text4.SetName("four five six.");
855   text4.AddState(ax::mojom::State::kIgnored);
856 
857   ui::AXNodeData text5;
858   text5.id = 115;
859   text5.role = ax::mojom::Role::kStaticText;
860   text5.SetName("five six seven.");
861 
862   ui::AXNodeData image;
863   image.id = 116;
864   image.role = ax::mojom::Role::kImage;
865 
866   ui::AXNodeData para1;
867   para1.id = 11;
868   para1.role = ax::mojom::Role::kParagraph;
869   para1.child_ids.push_back(text1.id);
870   para1.child_ids.push_back(text2.id);
871 
872   ui::AXNodeData para2;
873   para2.id = 12;
874   para2.role = ax::mojom::Role::kParagraph;
875   para2.child_ids.push_back(text4.id);
876   para2.child_ids.push_back(text5.id);
877   para2.AddState(ax::mojom::State::kIgnored);
878 
879   ui::AXNodeData button;
880   button.id = 13;
881   button.role = ax::mojom::Role::kButton;
882   button.child_ids.push_back(image.id);
883 
884   ui::AXNodeData root;
885   root.id = 1;
886   root.role = ax::mojom::Role::kRootWebArea;
887   root.child_ids.push_back(para1.id);
888   root.child_ids.push_back(text3.id);
889   root.child_ids.push_back(para2.id);
890   root.child_ids.push_back(button.id);
891 
892   std::unique_ptr<BrowserAccessibilityManager> manager(
893       BrowserAccessibilityManager::Create(
894           MakeAXTreeUpdate(root, para1, text1, text2, text3, para2, text4,
895                            text5, button, image),
896           test_browser_accessibility_delegate_.get()));
897 
898   BrowserAccessibility* root_obj = manager->GetRoot();
899   BrowserAccessibility* para_obj = root_obj->PlatformGetChild(0);
900   BrowserAccessibility* text1_obj = manager->GetFromID(111);
901   BrowserAccessibility* text2_obj = manager->GetFromID(112);
902   BrowserAccessibility* text3_obj = manager->GetFromID(113);
903   BrowserAccessibility* para2_obj = manager->GetFromID(12);
904   BrowserAccessibility* text4_obj = manager->GetFromID(114);
905   BrowserAccessibility* text5_obj = root_obj->PlatformGetChild(2);
906   BrowserAccessibility* button_obj = manager->GetFromID(13);
907   BrowserAccessibility* image_obj = manager->GetFromID(116);
908 
909   // Leaf nodes should have no children.
910   std::vector<gfx::NativeViewAccessible> descendants =
911       text1_obj->GetUIADescendants();
912   std::vector<gfx::NativeViewAccessible> expected_descendants = {};
913   EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
914 
915   descendants = text2_obj->GetUIADescendants();
916   EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
917 
918   descendants = text3_obj->GetUIADescendants();
919   EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
920 
921   descendants = text4_obj->GetUIADescendants();
922   EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
923 
924   descendants = text5_obj->GetUIADescendants();
925   EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
926 
927   descendants = para2_obj->GetUIADescendants();
928   EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
929 
930   descendants = image_obj->GetUIADescendants();
931   EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
932 
933   // Verify that para1 has two children (text1 and tex2).
934   descendants = para_obj->GetUIADescendants();
935   expected_descendants = {text1_obj->GetNativeViewAccessible(),
936                           text2_obj->GetNativeViewAccessible()};
937   EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
938 
939   // Verify that the button hides its child.
940   descendants = button_obj->GetUIADescendants();
941   expected_descendants = {};
942   EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
943 
944   // Calling GetChildNodeIds on the root should encompass the entire
945   // right and left subtrees (para1, text1, text2, and text3).
946   // para2 and its subtree should be ignored, except for text5. The image should
947   // be ignored, but not the button.
948   LOG(INFO) << "HERE";
949 
950   descendants = root_obj->GetUIADescendants();
951   expected_descendants = {para_obj->GetNativeViewAccessible(),
952                           text1_obj->GetNativeViewAccessible(),
953                           text2_obj->GetNativeViewAccessible(),
954                           text3_obj->GetNativeViewAccessible(),
955                           text5_obj->GetNativeViewAccessible(),
956                           button_obj->GetNativeViewAccessible()};
957   EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
958 
959   manager.reset();
960 }
961 
TEST_F(BrowserAccessibilityWinTest,TestCreateEmptyDocument)962 TEST_F(BrowserAccessibilityWinTest, TestCreateEmptyDocument) {
963   // Try creating an empty document with busy state. Readonly is
964   // set automatically.
965   std::unique_ptr<BrowserAccessibilityManager> manager(
966       new BrowserAccessibilityManagerWin(
967           BrowserAccessibilityManagerWin::GetEmptyDocument(),
968           test_browser_accessibility_delegate_.get()));
969 
970   // Verify the root is as we expect by default.
971   BrowserAccessibility* root = manager->GetRoot();
972   EXPECT_EQ(1, root->GetId());
973   EXPECT_EQ(ax::mojom::Role::kRootWebArea, root->GetRole());
974   EXPECT_EQ(0, root->GetState());
975   EXPECT_EQ(true, root->GetBoolAttribute(ax::mojom::BoolAttribute::kBusy));
976 
977   // Tree with a child textfield.
978   ui::AXNodeData tree1_1;
979   tree1_1.id = 1;
980   tree1_1.role = ax::mojom::Role::kRootWebArea;
981   tree1_1.child_ids.push_back(2);
982 
983   ui::AXNodeData tree1_2;
984   tree1_2.id = 2;
985   tree1_2.role = ax::mojom::Role::kTextField;
986 
987   // Process a load complete.
988   AXEventNotificationDetails event_bundle;
989   event_bundle.updates.resize(1);
990   event_bundle.updates[0].node_id_to_clear = root->GetId();
991   event_bundle.updates[0].root_id = tree1_1.id;
992   event_bundle.updates[0].nodes.push_back(tree1_1);
993   event_bundle.updates[0].nodes.push_back(tree1_2);
994   ASSERT_TRUE(manager->OnAccessibilityEvents(event_bundle));
995 
996   // The root should have been cleared,not replaced, because in the former case
997   // this could cause multiple focus and load complete events.
998   EXPECT_EQ(root, manager->GetRoot());
999 
1000   BrowserAccessibility* acc1_2 = manager->GetFromID(2);
1001   EXPECT_EQ(ax::mojom::Role::kTextField, acc1_2->GetRole());
1002   EXPECT_EQ(2, acc1_2->GetId());
1003 
1004   // Tree with a child button.
1005   ui::AXNodeData tree2_1;
1006   tree2_1.id = 1;
1007   tree2_1.role = ax::mojom::Role::kRootWebArea;
1008   tree2_1.child_ids.push_back(3);
1009 
1010   ui::AXNodeData tree2_2;
1011   tree2_2.id = 3;
1012   tree2_2.role = ax::mojom::Role::kButton;
1013 
1014   event_bundle.updates[0].nodes.clear();
1015   event_bundle.updates[0].node_id_to_clear = tree1_1.id;
1016   event_bundle.updates[0].root_id = tree2_1.id;
1017   event_bundle.updates[0].nodes.push_back(tree2_1);
1018   event_bundle.updates[0].nodes.push_back(tree2_2);
1019 
1020   // Unserialize the new tree update.
1021   ASSERT_TRUE(manager->OnAccessibilityEvents(event_bundle));
1022 
1023   // Verify that the root has been cleared, not replaced.
1024   EXPECT_EQ(root, manager->GetRoot());
1025 
1026   BrowserAccessibility* acc2_2 = manager->GetFromID(3);
1027   EXPECT_EQ(ax::mojom::Role::kButton, acc2_2->GetRole());
1028   EXPECT_EQ(3, acc2_2->GetId());
1029 
1030   // Ensure we properly cleaned up.
1031   manager.reset();
1032 }
1033 
GetUniqueId(BrowserAccessibility * accessibility)1034 int32_t GetUniqueId(BrowserAccessibility* accessibility) {
1035   BrowserAccessibilityWin* win_root = ToBrowserAccessibilityWin(accessibility);
1036   return win_root->GetCOM()->GetUniqueId();
1037 }
1038 
1039 // This is a regression test for a bug where the initial empty document
1040 // loaded by a BrowserAccessibilityManagerWin couldn't be looked up by
1041 // its UniqueIDWin, because the AX Tree was loaded in
1042 // BrowserAccessibilityManager code before BrowserAccessibilityManagerWin
1043 // was initialized.
TEST_F(BrowserAccessibilityWinTest,EmptyDocHasUniqueIdWin)1044 TEST_F(BrowserAccessibilityWinTest, EmptyDocHasUniqueIdWin) {
1045   std::unique_ptr<BrowserAccessibilityManagerWin> manager(
1046       new BrowserAccessibilityManagerWin(
1047           BrowserAccessibilityManagerWin::GetEmptyDocument(),
1048           test_browser_accessibility_delegate_.get()));
1049 
1050   // Verify the root is as we expect by default.
1051   BrowserAccessibility* root = manager->GetRoot();
1052   EXPECT_EQ(1, root->GetId());
1053   EXPECT_EQ(ax::mojom::Role::kRootWebArea, root->GetRole());
1054   EXPECT_EQ(0, root->GetState());
1055   EXPECT_EQ(true, root->GetBoolAttribute(ax::mojom::BoolAttribute::kBusy));
1056 
1057   BrowserAccessibilityWin* win_root = ToBrowserAccessibilityWin(root);
1058 
1059   ui::AXPlatformNode* node = static_cast<ui::AXPlatformNode*>(
1060       ui::AXPlatformNodeWin::GetFromUniqueId(GetUniqueId(win_root)));
1061 
1062   ui::AXPlatformNode* other_node =
1063       static_cast<ui::AXPlatformNode*>(win_root->GetCOM());
1064   ASSERT_EQ(node, other_node);
1065 }
1066 
TEST_F(BrowserAccessibilityWinTest,TestIA2Attributes)1067 TEST_F(BrowserAccessibilityWinTest, TestIA2Attributes) {
1068   ui::AXNodeData pseudo_before;
1069   pseudo_before.id = 2;
1070   pseudo_before.role = ax::mojom::Role::kGenericContainer;
1071   pseudo_before.AddStringAttribute(ax::mojom::StringAttribute::kHtmlTag,
1072                                    "<pseudo:before>");
1073   pseudo_before.AddStringAttribute(ax::mojom::StringAttribute::kDisplay,
1074                                    "none");
1075 
1076   ui::AXNodeData checkbox;
1077   checkbox.id = 3;
1078   checkbox.role = ax::mojom::Role::kCheckBox;
1079   checkbox.SetCheckedState(ax::mojom::CheckedState::kTrue);
1080   checkbox.SetName("Checkbox");
1081 
1082   ui::AXNodeData root;
1083   root.id = 1;
1084   root.role = ax::mojom::Role::kRootWebArea;
1085   root.AddState(ax::mojom::State::kFocusable);
1086   root.SetName("Document");
1087   root.child_ids.push_back(2);
1088   root.child_ids.push_back(3);
1089 
1090   std::unique_ptr<BrowserAccessibilityManager> manager(
1091       BrowserAccessibilityManager::Create(
1092           MakeAXTreeUpdate(root, pseudo_before, checkbox),
1093           test_browser_accessibility_delegate_.get()));
1094 
1095   ASSERT_NE(nullptr, manager->GetRoot());
1096   BrowserAccessibilityWin* root_accessible =
1097       ToBrowserAccessibilityWin(manager->GetRoot());
1098   ASSERT_NE(nullptr, root_accessible);
1099   ASSERT_EQ(2U, root_accessible->PlatformChildCount());
1100 
1101   BrowserAccessibilityWin* pseudo_accessible =
1102       ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(0));
1103   ASSERT_NE(nullptr, pseudo_accessible);
1104 
1105   base::win::ScopedBstr attributes;
1106   HRESULT hr =
1107       pseudo_accessible->GetCOM()->get_attributes(attributes.Receive());
1108   EXPECT_EQ(S_OK, hr);
1109   EXPECT_NE(nullptr, attributes.Get());
1110   std::wstring attributes_str(attributes.Get(), attributes.Length());
1111   EXPECT_EQ(L"display:none;tag:<pseudo\\:before>;", attributes_str);
1112 
1113   BrowserAccessibilityWin* checkbox_accessible =
1114       ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(1));
1115   ASSERT_NE(nullptr, checkbox_accessible);
1116 
1117   attributes.Reset();
1118   hr = checkbox_accessible->GetCOM()->get_attributes(attributes.Receive());
1119   EXPECT_EQ(S_OK, hr);
1120   EXPECT_NE(nullptr, attributes.Get());
1121   attributes_str = std::wstring(attributes.Get(), attributes.Length());
1122   EXPECT_EQ(L"checkable:true;explicit-name:true;", attributes_str);
1123 
1124   manager.reset();
1125 }
1126 
TEST_F(BrowserAccessibilityWinTest,TestValueAttributeInTextControls)1127 TEST_F(BrowserAccessibilityWinTest, TestValueAttributeInTextControls) {
1128   ui::AXNodeData root;
1129   root.id = 1;
1130   root.role = ax::mojom::Role::kRootWebArea;
1131   root.AddState(ax::mojom::State::kFocusable);
1132 
1133   ui::AXNodeData combo_box, combo_box_text;
1134   combo_box.id = 2;
1135   combo_box_text.id = 3;
1136   combo_box.role = ax::mojom::Role::kTextFieldWithComboBox;
1137   combo_box_text.role = ax::mojom::Role::kStaticText;
1138   combo_box.SetName("Combo box:");
1139   combo_box_text.SetName("Combo box text");
1140   combo_box.AddBoolAttribute(ax::mojom::BoolAttribute::kEditableRoot, true);
1141   combo_box.AddState(ax::mojom::State::kEditable);
1142   combo_box.AddState(ax::mojom::State::kRichlyEditable);
1143   combo_box.AddState(ax::mojom::State::kFocusable);
1144   combo_box_text.AddState(ax::mojom::State::kEditable);
1145   combo_box_text.AddState(ax::mojom::State::kRichlyEditable);
1146   combo_box.child_ids.push_back(combo_box_text.id);
1147 
1148   ui::AXNodeData search_box, search_box_text, new_line;
1149   search_box.id = 4;
1150   search_box_text.id = 5;
1151   new_line.id = 6;
1152   search_box.role = ax::mojom::Role::kSearchBox;
1153   search_box_text.role = ax::mojom::Role::kStaticText;
1154   new_line.role = ax::mojom::Role::kLineBreak;
1155   search_box.SetName("Search for:");
1156   search_box_text.SetName("Search box text");
1157   new_line.SetName("\n");
1158   search_box.AddBoolAttribute(ax::mojom::BoolAttribute::kEditableRoot, true);
1159   search_box.AddState(ax::mojom::State::kEditable);
1160   search_box.AddState(ax::mojom::State::kRichlyEditable);
1161   search_box.AddState(ax::mojom::State::kFocusable);
1162   search_box_text.AddState(ax::mojom::State::kEditable);
1163   search_box_text.AddState(ax::mojom::State::kRichlyEditable);
1164   new_line.AddState(ax::mojom::State::kEditable);
1165   new_line.AddState(ax::mojom::State::kRichlyEditable);
1166   search_box.child_ids.push_back(search_box_text.id);
1167   search_box.child_ids.push_back(new_line.id);
1168 
1169   ui::AXNodeData text_field;
1170   text_field.id = 7;
1171   text_field.role = ax::mojom::Role::kTextField;
1172   text_field.AddBoolAttribute(ax::mojom::BoolAttribute::kEditableRoot, true);
1173   text_field.AddState(ax::mojom::State::kEditable);
1174   text_field.AddState(ax::mojom::State::kFocusable);
1175   text_field.SetValue("Text field text");
1176 
1177   ui::AXNodeData link, link_text;
1178   link.id = 8;
1179   link_text.id = 9;
1180   link.role = ax::mojom::Role::kLink;
1181   link_text.role = ax::mojom::Role::kStaticText;
1182   link_text.SetName("Link text");
1183   link.child_ids.push_back(link_text.id);
1184 
1185   ui::AXNodeData slider, slider_text;
1186   slider.id = 10;
1187   slider_text.id = 11;
1188   slider.role = ax::mojom::Role::kSlider;
1189   slider_text.role = ax::mojom::Role::kStaticText;
1190   slider.AddFloatAttribute(ax::mojom::FloatAttribute::kValueForRange, 5.0F);
1191   slider_text.SetName("Slider text");
1192   slider.child_ids.push_back(slider_text.id);
1193 
1194   root.child_ids.push_back(2);   // Combo box.
1195   root.child_ids.push_back(4);   // Search box.
1196   root.child_ids.push_back(7);   // Text field.
1197   root.child_ids.push_back(8);   // Link.
1198   root.child_ids.push_back(10);  // Slider.
1199 
1200   std::unique_ptr<BrowserAccessibilityManager> manager(
1201       BrowserAccessibilityManager::Create(
1202           MakeAXTreeUpdate(root, combo_box, combo_box_text, search_box,
1203                            search_box_text, new_line, text_field, link,
1204                            link_text, slider, slider_text),
1205           test_browser_accessibility_delegate_.get()));
1206 
1207   ASSERT_NE(nullptr, manager->GetRoot());
1208   BrowserAccessibilityWin* root_accessible =
1209       ToBrowserAccessibilityWin(manager->GetRoot());
1210   ASSERT_NE(nullptr, root_accessible);
1211   ASSERT_EQ(5U, root_accessible->PlatformChildCount());
1212 
1213   BrowserAccessibilityWin* combo_box_accessible =
1214       ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(0));
1215   ASSERT_NE(nullptr, combo_box_accessible);
1216   manager->SetFocusLocallyForTesting(combo_box_accessible);
1217   ASSERT_EQ(combo_box_accessible,
1218             ToBrowserAccessibilityWin(manager->GetFocus()));
1219   BrowserAccessibilityWin* search_box_accessible =
1220       ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(1));
1221   ASSERT_NE(nullptr, search_box_accessible);
1222   BrowserAccessibilityWin* text_field_accessible =
1223       ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(2));
1224   ASSERT_NE(nullptr, text_field_accessible);
1225   BrowserAccessibilityWin* link_accessible =
1226       ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(3));
1227   ASSERT_NE(nullptr, link_accessible);
1228   BrowserAccessibilityWin* slider_accessible =
1229       ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(4));
1230   ASSERT_NE(nullptr, slider_accessible);
1231 
1232   base::win::ScopedVariant childid_self(CHILDID_SELF);
1233   base::win::ScopedVariant childid_slider(5);
1234   base::win::ScopedBstr value;
1235 
1236   HRESULT hr = combo_box_accessible->GetCOM()->get_accValue(childid_self,
1237                                                             value.Receive());
1238   EXPECT_EQ(S_OK, hr);
1239   EXPECT_STREQ(L"Combo box text", value.Get());
1240   value.Reset();
1241   hr = search_box_accessible->GetCOM()->get_accValue(childid_self,
1242                                                      value.Receive());
1243   EXPECT_EQ(S_OK, hr);
1244   EXPECT_STREQ(L"Search box text\n", value.Get());
1245   value.Reset();
1246   hr = text_field_accessible->GetCOM()->get_accValue(childid_self,
1247                                                      value.Receive());
1248   EXPECT_EQ(S_OK, hr);
1249   EXPECT_STREQ(L"Text field text", value.Get());
1250   value.Reset();
1251 
1252   // Other controls, such as links, should not use their inner text as their
1253   // value. Only text entry controls.
1254   hr = link_accessible->GetCOM()->get_accValue(childid_self, value.Receive());
1255   EXPECT_EQ(S_OK, hr);
1256   EXPECT_EQ(0u, value.Length());
1257   value.Reset();
1258 
1259   // Sliders and other range controls should expose their current value and not
1260   // their inner text.
1261   // Also, try accessing the slider via its child number instead of directly.
1262   hr = root_accessible->GetCOM()->get_accValue(childid_slider, value.Receive());
1263   EXPECT_EQ(S_OK, hr);
1264   EXPECT_STREQ(L"5", value.Get());
1265   value.Reset();
1266 
1267   manager.reset();
1268 }
1269 
TEST_F(BrowserAccessibilityWinTest,TestWordBoundariesInTextControls)1270 TEST_F(BrowserAccessibilityWinTest, TestWordBoundariesInTextControls) {
1271   const base::string16 line1(L"This is a very LONG line of text that ");
1272   const base::string16 line2(L"should wrap on more than one lines ");
1273   const base::string16 text(line1 + line2);
1274 
1275   std::vector<int32_t> line1_word_starts;
1276   line1_word_starts.push_back(0);
1277   line1_word_starts.push_back(5);
1278   line1_word_starts.push_back(8);
1279   line1_word_starts.push_back(10);
1280   line1_word_starts.push_back(15);
1281   line1_word_starts.push_back(20);
1282   line1_word_starts.push_back(25);
1283   line1_word_starts.push_back(28);
1284   line1_word_starts.push_back(33);
1285 
1286   std::vector<int32_t> line2_word_starts;
1287   line2_word_starts.push_back(0);
1288   line2_word_starts.push_back(7);
1289   line2_word_starts.push_back(12);
1290   line2_word_starts.push_back(15);
1291   line2_word_starts.push_back(20);
1292   line2_word_starts.push_back(25);
1293   line2_word_starts.push_back(29);
1294 
1295   ui::AXNodeData root;
1296   root.id = 1;
1297   root.role = ax::mojom::Role::kRootWebArea;
1298   root.AddState(ax::mojom::State::kFocusable);
1299 
1300   ui::AXNodeData textarea, textarea_div, textarea_text;
1301   textarea.id = 2;
1302   textarea_div.id = 3;
1303   textarea_text.id = 4;
1304   textarea.role = ax::mojom::Role::kTextField;
1305   textarea_div.role = ax::mojom::Role::kGenericContainer;
1306   textarea_text.role = ax::mojom::Role::kStaticText;
1307   textarea.AddState(ax::mojom::State::kEditable);
1308   textarea.AddState(ax::mojom::State::kFocusable);
1309   textarea.AddState(ax::mojom::State::kMultiline);
1310   textarea_div.AddState(ax::mojom::State::kEditable);
1311   textarea_text.AddState(ax::mojom::State::kEditable);
1312   textarea.SetValue(base::UTF16ToUTF8(text));
1313   textarea_text.SetName(base::UTF16ToUTF8(text));
1314   textarea.AddStringAttribute(ax::mojom::StringAttribute::kHtmlTag, "textarea");
1315   textarea.child_ids.push_back(textarea_div.id);
1316   textarea_div.child_ids.push_back(textarea_text.id);
1317 
1318   ui::AXNodeData textarea_line1, textarea_line2;
1319   textarea_line1.id = 5;
1320   textarea_line2.id = 6;
1321   textarea_line1.role = ax::mojom::Role::kInlineTextBox;
1322   textarea_line2.role = ax::mojom::Role::kInlineTextBox;
1323   textarea_line1.AddState(ax::mojom::State::kEditable);
1324   textarea_line2.AddState(ax::mojom::State::kEditable);
1325   textarea_line1.SetName(base::UTF16ToUTF8(line1));
1326   textarea_line2.SetName(base::UTF16ToUTF8(line2));
1327   textarea_line1.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
1328                                      line1_word_starts);
1329   textarea_line2.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
1330                                      line2_word_starts);
1331   textarea_text.child_ids.push_back(textarea_line1.id);
1332   textarea_text.child_ids.push_back(textarea_line2.id);
1333 
1334   ui::AXNodeData text_field, text_field_div, text_field_text;
1335   text_field.id = 7;
1336   text_field_div.id = 8;
1337   text_field_text.id = 9;
1338   text_field.role = ax::mojom::Role::kTextField;
1339   text_field_div.role = ax::mojom::Role::kGenericContainer;
1340   text_field_text.role = ax::mojom::Role::kStaticText;
1341   text_field.AddState(ax::mojom::State::kEditable);
1342   text_field.AddState(ax::mojom::State::kFocusable);
1343   text_field_div.AddState(ax::mojom::State::kEditable);
1344   text_field_text.AddState(ax::mojom::State::kEditable);
1345   text_field.SetValue(base::UTF16ToUTF8(line1));
1346   text_field_text.SetName(base::UTF16ToUTF8(line1));
1347   text_field.AddStringAttribute(ax::mojom::StringAttribute::kHtmlTag, "input");
1348   text_field.html_attributes.push_back(std::make_pair("type", "text"));
1349   text_field.child_ids.push_back(text_field_div.id);
1350   text_field_div.child_ids.push_back(text_field_text.id);
1351 
1352   ui::AXNodeData text_field_line;
1353   text_field_line.id = 10;
1354   text_field_line.role = ax::mojom::Role::kInlineTextBox;
1355   text_field_line.AddState(ax::mojom::State::kEditable);
1356   text_field_line.SetName(base::UTF16ToUTF8(line1));
1357   text_field_line.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
1358                                       line1_word_starts);
1359   text_field_text.child_ids.push_back(text_field_line.id);
1360 
1361   root.child_ids.push_back(2);  // Textarea.
1362   root.child_ids.push_back(7);  // Text field.
1363 
1364   std::unique_ptr<BrowserAccessibilityManager> manager(
1365       BrowserAccessibilityManager::Create(
1366           MakeAXTreeUpdate(root, textarea, textarea_div, textarea_text,
1367                            textarea_line1, textarea_line2, text_field,
1368                            text_field_div, text_field_text, text_field_line),
1369           test_browser_accessibility_delegate_.get()));
1370 
1371   ASSERT_NE(nullptr, manager->GetRoot());
1372   BrowserAccessibilityWin* root_accessible =
1373       ToBrowserAccessibilityWin(manager->GetRoot());
1374   ASSERT_NE(nullptr, root_accessible);
1375   ASSERT_EQ(2U, root_accessible->PlatformChildCount());
1376 
1377   BrowserAccessibilityWin* textarea_accessible =
1378       ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(0));
1379   ASSERT_NE(nullptr, textarea_accessible);
1380   BrowserAccessibilityWin* text_field_accessible =
1381       ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(1));
1382   ASSERT_NE(nullptr, text_field_accessible);
1383 
1384   Microsoft::WRL::ComPtr<IAccessibleText> textarea_object;
1385   EXPECT_HRESULT_SUCCEEDED(textarea_accessible->GetCOM()->QueryInterface(
1386       IID_PPV_ARGS(&textarea_object)));
1387   Microsoft::WRL::ComPtr<IAccessibleText> text_field_object;
1388   EXPECT_HRESULT_SUCCEEDED(text_field_accessible->GetCOM()->QueryInterface(
1389       IID_PPV_ARGS(&text_field_object)));
1390 
1391   LONG offset = 0;
1392   while (offset < static_cast<LONG>(text.length())) {
1393     LONG start, end;
1394     base::win::ScopedBstr word;
1395     EXPECT_EQ(S_OK,
1396               textarea_object->get_textAtOffset(offset, IA2_TEXT_BOUNDARY_WORD,
1397                                                 &start, &end, word.Receive()));
1398     EXPECT_EQ(offset, start);
1399     EXPECT_LT(offset, end);
1400     LONG space_offset = static_cast<LONG>(text.find(' ', offset));
1401     EXPECT_EQ(space_offset + 1, end);
1402     LONG length = end - start;
1403     EXPECT_STREQ(text.substr(start, length).c_str(), word.Get());
1404     word.Reset();
1405     offset = end;
1406   }
1407 
1408   offset = 0;
1409   while (offset < static_cast<LONG>(line1.length())) {
1410     LONG start, end;
1411     base::win::ScopedBstr word;
1412     EXPECT_EQ(S_OK, text_field_object->get_textAtOffset(
1413                         offset, IA2_TEXT_BOUNDARY_WORD, &start, &end,
1414                         word.Receive()));
1415     EXPECT_EQ(offset, start);
1416     EXPECT_LT(offset, end);
1417     LONG space_offset = static_cast<LONG>(line1.find(' ', offset));
1418     EXPECT_EQ(space_offset + 1, end);
1419     LONG length = end - start;
1420     EXPECT_STREQ(text.substr(start, length).c_str(), word.Get());
1421     word.Reset();
1422     offset = end;
1423   }
1424 
1425   textarea_object.Reset();
1426   text_field_object.Reset();
1427 
1428   manager.reset();
1429 }
1430 
TEST_F(BrowserAccessibilityWinTest,TextBoundariesOnlyEmbeddedObjectsNoCrash)1431 TEST_F(BrowserAccessibilityWinTest, TextBoundariesOnlyEmbeddedObjectsNoCrash) {
1432   // Update the tree structure to test get_textAtOffset from an
1433   // embedded object that has no text, only an embedded object child.
1434   //
1435   // +-1 root_data
1436   //   +-2 menu_data
1437   //   | +-3 button_1_data
1438   //   | +-4 button_2_data
1439   //   +-5 static_text_data "after"
1440   //
1441   ui::AXNodeData root_data;
1442   root_data.id = 1;
1443   root_data.role = ax::mojom::Role::kRootWebArea;
1444 
1445   ui::AXNodeData menu_data;
1446   menu_data.id = 2;
1447   menu_data.role = ax::mojom::Role::kMenu;
1448 
1449   ui::AXNodeData button_1_data;
1450   button_1_data.id = 3;
1451   button_1_data.role = ax::mojom::Role::kButton;
1452 
1453   ui::AXNodeData button_2_data;
1454   button_2_data.id = 4;
1455   button_2_data.role = ax::mojom::Role::kButton;
1456 
1457   ui::AXNodeData static_text_data;
1458   static_text_data.id = 5;
1459   static_text_data.role = ax::mojom::Role::kStaticText;
1460   static_text_data.SetName("after");
1461 
1462   root_data.child_ids = {menu_data.id, static_text_data.id};
1463   menu_data.child_ids = {button_1_data.id, button_2_data.id};
1464 
1465   std::unique_ptr<BrowserAccessibilityManager> manager(
1466       BrowserAccessibilityManager::Create(
1467           MakeAXTreeUpdate(root_data, menu_data, button_1_data, button_2_data,
1468                            static_text_data),
1469           test_browser_accessibility_delegate_.get()));
1470 
1471   ASSERT_NE(nullptr, manager->GetRoot());
1472   BrowserAccessibilityWin* root_accessible =
1473       ToBrowserAccessibilityWin(manager->GetRoot());
1474   ASSERT_NE(nullptr, root_accessible);
1475   ASSERT_EQ(2U, root_accessible->PlatformChildCount());
1476 
1477   BrowserAccessibilityComWin* menu_accessible_com =
1478       ToBrowserAccessibilityComWin(root_accessible->PlatformGetChild(0));
1479   ASSERT_NE(nullptr, menu_accessible_com);
1480   ASSERT_EQ(ax::mojom::Role::kMenu, menu_accessible_com->GetData().role);
1481 
1482   // TODO(crbug.com/1039528): This should not have 2 embedded object characters.
1483   {
1484     const std::array<base::char16, 2> pieces = {
1485         ui::AXPlatformNodeBase::kEmbeddedCharacter,
1486         ui::AXPlatformNodeBase::kEmbeddedCharacter};
1487     const base::string16 expect(pieces.cbegin(), pieces.cend());
1488     EXPECT_IA2_TEXT_AT_OFFSET(menu_accessible_com, 0, IA2_TEXT_BOUNDARY_CHAR,
1489                               /*expected_hr=*/S_OK, /*start=*/0, /*end=*/2,
1490                               /*text=*/expect.c_str());
1491   }
1492 }
1493 
TEST_F(BrowserAccessibilityWinTest,TestTextBoundariesEmbeddedCharacterText)1494 TEST_F(BrowserAccessibilityWinTest, TestTextBoundariesEmbeddedCharacterText) {
1495   // Update the tree structure to test empty leaf text positions.
1496   //
1497   // +-1 root_data
1498   //   +-2 body_data
1499   //     +-3 static_text_1_data "before"
1500   //     | +-4 inline_text_1_data "before"
1501   //     +-5 menu_data_1
1502   //     | +-6 button_data
1503   //     |   +-7 button_leaf_container_data
1504   //     |   +-8 button_leaf_svg_data
1505   //     +-9 menu_data_2
1506   //     +-10 static_text_2_data "after"
1507   //     | +-11 inline_text_2_data "after"
1508   //     +-12 static_text_3_data "tail"
1509   //     | +-13 inline_text_3_data "tail"
1510   //
1511   ui::AXNodeData root_data;
1512   root_data.id = 1;
1513   root_data.role = ax::mojom::Role::kRootWebArea;
1514 
1515   ui::AXNodeData body_data;
1516   body_data.id = 2;
1517   body_data.role = ax::mojom::Role::kGenericContainer;
1518 
1519   ui::AXNodeData static_text_1_data;
1520   static_text_1_data.id = 3;
1521   static_text_1_data.role = ax::mojom::Role::kStaticText;
1522   static_text_1_data.SetName("before");
1523 
1524   ui::AXNodeData inline_text_1_data;
1525   inline_text_1_data.id = 4;
1526   inline_text_1_data.role = ax::mojom::Role::kInlineTextBox;
1527   inline_text_1_data.SetName("before");
1528 
1529   ui::AXNodeData menu_data_1;
1530   menu_data_1.id = 5;
1531   menu_data_1.role = ax::mojom::Role::kMenu;
1532 
1533   ui::AXNodeData button_data;
1534   button_data.id = 6;
1535   button_data.role = ax::mojom::Role::kButton;
1536 
1537   ui::AXNodeData button_leaf_container_data;
1538   button_leaf_container_data.id = 7;
1539   button_leaf_container_data.role = ax::mojom::Role::kGenericContainer;
1540 
1541   ui::AXNodeData button_leaf_svg_data;
1542   button_leaf_svg_data.id = 8;
1543   button_leaf_svg_data.role = ax::mojom::Role::kSvgRoot;
1544 
1545   ui::AXNodeData menu_data_2;
1546   menu_data_2.id = 9;
1547   menu_data_2.role = ax::mojom::Role::kMenu;
1548 
1549   ui::AXNodeData static_text_2_data;
1550   static_text_2_data.id = 10;
1551   static_text_2_data.role = ax::mojom::Role::kStaticText;
1552   static_text_2_data.SetName("after");
1553 
1554   ui::AXNodeData inline_text_2_data;
1555   inline_text_2_data.id = 11;
1556   inline_text_2_data.role = ax::mojom::Role::kInlineTextBox;
1557   inline_text_2_data.SetName("after");
1558 
1559   ui::AXNodeData static_text_3_data;
1560   static_text_3_data.id = 12;
1561   static_text_3_data.role = ax::mojom::Role::kStaticText;
1562   static_text_3_data.SetName("tail");
1563 
1564   ui::AXNodeData inline_text_3_data;
1565   inline_text_3_data.id = 13;
1566   inline_text_3_data.role = ax::mojom::Role::kInlineTextBox;
1567   inline_text_3_data.SetName("tail");
1568 
1569   root_data.child_ids = {body_data.id};
1570   body_data.child_ids = {static_text_1_data.id, menu_data_1.id, menu_data_2.id,
1571                          static_text_2_data.id, static_text_3_data.id};
1572   menu_data_1.child_ids = {button_data.id};
1573   button_data.child_ids = {button_leaf_container_data.id,
1574                            button_leaf_svg_data.id};
1575   static_text_1_data.child_ids = {inline_text_1_data.id};
1576   static_text_2_data.child_ids = {inline_text_2_data.id};
1577   static_text_3_data.child_ids = {inline_text_3_data.id};
1578 
1579   ui::AXTreeUpdate update;
1580   ui::AXTreeData tree_data;
1581   tree_data.tree_id = ui::AXTreeID::CreateNewAXTreeID();
1582   tree_data.focused_tree_id = tree_data.tree_id;
1583   update.tree_data = tree_data;
1584   update.has_tree_data = true;
1585   update.root_id = root_data.id;
1586   update.nodes = {root_data,
1587                   body_data,
1588                   static_text_1_data,
1589                   inline_text_1_data,
1590                   menu_data_1,
1591                   button_data,
1592                   button_leaf_container_data,
1593                   button_leaf_svg_data,
1594                   menu_data_2,
1595                   static_text_2_data,
1596                   inline_text_2_data,
1597                   static_text_3_data,
1598                   inline_text_3_data};
1599 
1600   std::unique_ptr<BrowserAccessibilityManager> manager(
1601       BrowserAccessibilityManager::Create(
1602           update, test_browser_accessibility_delegate_.get()));
1603 
1604   ASSERT_NE(nullptr, manager->GetRoot());
1605   BrowserAccessibilityWin* root_accessible =
1606       ToBrowserAccessibilityWin(manager->GetRoot());
1607   ASSERT_NE(nullptr, root_accessible);
1608   ASSERT_EQ(1U, root_accessible->PlatformChildCount());
1609 
1610   BrowserAccessibilityWin* body_accessible =
1611       ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(0));
1612   ASSERT_NE(nullptr, body_accessible);
1613   ASSERT_EQ(5U, body_accessible->PlatformChildCount());
1614   BrowserAccessibilityComWin* body_accessible_com = body_accessible->GetCOM();
1615   ASSERT_NE(nullptr, body_accessible_com);
1616 
1617   BrowserAccessibilityComWin* static_text_1_com =
1618       ToBrowserAccessibilityWin(body_accessible->PlatformGetChild(0))->GetCOM();
1619   ASSERT_NE(nullptr, static_text_1_com);
1620   ASSERT_EQ(ax::mojom::Role::kStaticText, static_text_1_com->GetData().role);
1621 
1622   BrowserAccessibilityComWin* menu_1_accessible_com =
1623       ToBrowserAccessibilityWin(body_accessible->PlatformGetChild(1))->GetCOM();
1624   ASSERT_NE(nullptr, menu_1_accessible_com);
1625   ASSERT_EQ(ax::mojom::Role::kMenu, menu_1_accessible_com->GetData().role);
1626 
1627   BrowserAccessibilityComWin* menu_2_accessible_com =
1628       ToBrowserAccessibilityWin(body_accessible->PlatformGetChild(2))->GetCOM();
1629   ASSERT_NE(nullptr, menu_2_accessible_com);
1630   ASSERT_EQ(ax::mojom::Role::kMenu, menu_2_accessible_com->GetData().role);
1631 
1632   BrowserAccessibilityComWin* static_text_2_com =
1633       ToBrowserAccessibilityWin(body_accessible->PlatformGetChild(3))->GetCOM();
1634   ASSERT_NE(nullptr, static_text_2_com);
1635   ASSERT_EQ(ax::mojom::Role::kStaticText, static_text_2_com->GetData().role);
1636 
1637   BrowserAccessibilityComWin* static_text_3_com =
1638       ToBrowserAccessibilityWin(body_accessible->PlatformGetChild(4))->GetCOM();
1639   ASSERT_NE(nullptr, static_text_3_com);
1640   ASSERT_EQ(ax::mojom::Role::kStaticText, static_text_3_com->GetData().role);
1641 
1642   // L"<b>efore" [obj] [obj] L"after" L"tail"
1643   EXPECT_IA2_TEXT_AT_OFFSET(body_accessible_com, 0, IA2_TEXT_BOUNDARY_CHAR,
1644                             /*expected_hr=*/S_OK, /*start=*/0, /*end=*/1,
1645                             /*text=*/L"b");
1646 
1647   // L"bef<o>re" [obj] [obj] L"after" L"tail"
1648   EXPECT_IA2_TEXT_AT_OFFSET(body_accessible_com, 3, IA2_TEXT_BOUNDARY_CHAR,
1649                             /*expected_hr=*/S_OK, /*start=*/3, /*end=*/4,
1650                             /*text=*/L"o");
1651 
1652   // L"befor<e>" [obj] [obj] L"after" L"tail"
1653   EXPECT_IA2_TEXT_AT_OFFSET(body_accessible_com, 5, IA2_TEXT_BOUNDARY_CHAR,
1654                             /*expected_hr=*/S_OK, /*start=*/5, /*end=*/6,
1655                             /*text=*/L"e");
1656 
1657   // L"before" <[obj]> [obj] L"after" L"tail"
1658   // TODO(crbug.com/1039528): This should not include multiple characters.
1659   {
1660     const std::array<base::char16, 3> pieces = {
1661         ui::AXPlatformNodeBase::kEmbeddedCharacter,
1662         ui::AXPlatformNodeBase::kEmbeddedCharacter, L'a'};
1663     const base::string16 expect(pieces.cbegin(), pieces.cend());
1664     EXPECT_IA2_TEXT_AT_OFFSET(body_accessible_com, 6, IA2_TEXT_BOUNDARY_CHAR,
1665                               /*expected_hr=*/S_OK, /*start=*/6, /*end=*/9,
1666                               /*text=*/
1667                               expect.c_str());
1668   }
1669 
1670   // L"before" [obj] <[obj]> L"after" L"tail"
1671   // TODO(crbug.com/1039528): This should not include multiple characters.
1672   {
1673     const std::array<base::char16, 2> pieces = {
1674         ui::AXPlatformNodeBase::kEmbeddedCharacter, L'a'};
1675     const base::string16 expect(pieces.cbegin(), pieces.cend());
1676     EXPECT_IA2_TEXT_AT_OFFSET(body_accessible_com, 7, IA2_TEXT_BOUNDARY_CHAR,
1677                               /*expected_hr=*/S_OK, /*start=*/7, /*end=*/9,
1678                               /*text=*/
1679                               expect.c_str());
1680   }
1681 
1682   // L"before" [obj] [obj] L"<a>fter" L"tail"
1683   EXPECT_IA2_TEXT_AT_OFFSET(body_accessible_com, 8, IA2_TEXT_BOUNDARY_CHAR,
1684                             /*expected_hr=*/S_OK, /*start=*/8, /*end=*/9,
1685                             /*text=*/L"a");
1686 
1687   // L"before" [obj] [obj] L"a<f>ter" L"tail"
1688   EXPECT_IA2_TEXT_AT_OFFSET(body_accessible_com, 9, IA2_TEXT_BOUNDARY_CHAR,
1689                             /*expected_hr=*/S_OK, /*start=*/9, /*end=*/10,
1690                             /*text=*/L"f");
1691 
1692   // L"before" [obj] [obj] L"afte<r>" L"tail"
1693   EXPECT_IA2_TEXT_AT_OFFSET(body_accessible_com, 12, IA2_TEXT_BOUNDARY_CHAR,
1694                             /*expected_hr=*/S_OK, /*start=*/12, /*end=*/13,
1695                             /*text=*/L"r");
1696 
1697   // L"before" [obj] [obj] L"after" L"<t>ail"
1698   EXPECT_IA2_TEXT_AT_OFFSET(body_accessible_com, 13, IA2_TEXT_BOUNDARY_CHAR,
1699                             /*expected_hr=*/S_OK, /*start=*/13, /*end=*/14,
1700                             /*text=*/L"t");
1701 
1702   // L"before" [obj] [obj] L"after" L"ta<i>l"
1703   EXPECT_IA2_TEXT_AT_OFFSET(body_accessible_com, 15, IA2_TEXT_BOUNDARY_CHAR,
1704                             /*expected_hr=*/S_OK, /*start=*/15, /*end=*/16,
1705                             /*text=*/L"i");
1706 
1707   // L"before" [obj] [obj] L"after" L"tai<l>"
1708   EXPECT_IA2_TEXT_AT_OFFSET(body_accessible_com, 16, IA2_TEXT_BOUNDARY_CHAR,
1709                             /*expected_hr=*/S_OK, /*start=*/16, /*end=*/17,
1710                             /*text=*/L"l");
1711 
1712   // L"before" [obj] [obj] L"after" L"tail<>"
1713   EXPECT_IA2_TEXT_AT_OFFSET(body_accessible_com, 17, IA2_TEXT_BOUNDARY_CHAR,
1714                             /*expected_hr=*/E_INVALIDARG, /*start=*/0,
1715                             /*end=*/0,
1716                             /*text=*/nullptr);
1717 
1718   // L"<b>efore"
1719   EXPECT_IA2_TEXT_AT_OFFSET(static_text_1_com, 0, IA2_TEXT_BOUNDARY_CHAR,
1720                             /*expected_hr=*/S_OK, /*start=*/0, /*end=*/1,
1721                             /*text=*/L"b");
1722 
1723   // L"be<f>ore"
1724   EXPECT_IA2_TEXT_AT_OFFSET(static_text_1_com, 2, IA2_TEXT_BOUNDARY_CHAR,
1725                             /*expected_hr=*/S_OK, /*start=*/2, /*end=*/3,
1726                             /*text=*/L"f");
1727 
1728   // L"before<>"
1729   EXPECT_IA2_TEXT_AT_OFFSET(static_text_1_com, 6, IA2_TEXT_BOUNDARY_CHAR,
1730                             /*expected_hr=*/E_INVALIDARG, /*start=*/0,
1731                             /*end=*/0,
1732                             /*text=*/nullptr);
1733 
1734   // <[obj]>
1735   EXPECT_IA2_TEXT_AT_OFFSET(
1736       menu_1_accessible_com, 0, IA2_TEXT_BOUNDARY_CHAR,
1737       /*expected_hr=*/S_OK, /*start=*/0, /*end=*/1,
1738       /*text=*/
1739       base::string16{ui::AXPlatformNodeBase::kEmbeddedCharacter}.c_str());
1740 
1741   // [obj]<>
1742   EXPECT_IA2_TEXT_AT_OFFSET(menu_1_accessible_com, 1, IA2_TEXT_BOUNDARY_CHAR,
1743                             /*expected_hr=*/E_INVALIDARG, /*start=*/0,
1744                             /*end=*/0,
1745                             /*text=*/nullptr);
1746 
1747   // L"<>"
1748   EXPECT_IA2_TEXT_AT_OFFSET(menu_2_accessible_com, 0, IA2_TEXT_BOUNDARY_CHAR,
1749                             /*expected_hr=*/E_INVALIDARG, /*start=*/0,
1750                             /*end=*/0,
1751                             /*text=*/nullptr);
1752 
1753   // L"<a>fter"
1754   EXPECT_IA2_TEXT_AT_OFFSET(static_text_2_com, 0, IA2_TEXT_BOUNDARY_CHAR,
1755                             /*expected_hr=*/S_OK, /*start=*/0, /*end=*/1,
1756                             /*text=*/L"a");
1757 
1758   // L"af<t>er"
1759   EXPECT_IA2_TEXT_AT_OFFSET(static_text_2_com, 2, IA2_TEXT_BOUNDARY_CHAR,
1760                             /*expected_hr=*/S_OK, /*start=*/2, /*end=*/3,
1761                             /*text=*/L"t");
1762 
1763   // L"after<>"
1764   EXPECT_IA2_TEXT_AT_OFFSET(static_text_2_com, 5, IA2_TEXT_BOUNDARY_CHAR,
1765                             /*expected_hr=*/E_INVALIDARG, /*start=*/0,
1766                             /*end=*/0,
1767                             /*text=*/nullptr);
1768 
1769   // L"<t>ail"
1770   EXPECT_IA2_TEXT_AT_OFFSET(static_text_3_com, 0, IA2_TEXT_BOUNDARY_CHAR,
1771                             /*expected_hr=*/S_OK, /*start=*/0, /*end=*/1,
1772                             /*text=*/L"t");
1773 
1774   // L"ta<i>l"
1775   EXPECT_IA2_TEXT_AT_OFFSET(static_text_3_com, 2, IA2_TEXT_BOUNDARY_CHAR,
1776                             /*expected_hr=*/S_OK, /*start=*/2, /*end=*/3,
1777                             /*text=*/L"i");
1778 
1779   // L"tail<>"
1780   EXPECT_IA2_TEXT_AT_OFFSET(static_text_3_com, 4, IA2_TEXT_BOUNDARY_CHAR,
1781                             /*expected_hr=*/E_INVALIDARG, /*start=*/0,
1782                             /*end=*/0,
1783                             /*text=*/nullptr);
1784 
1785   // L"before" [obj] <[obj]> L"<a>fter" L"tail"
1786   EXPECT_IA2_TEXT_BEFORE_OFFSET(
1787       body_accessible_com, 7, IA2_TEXT_BOUNDARY_CHAR,
1788       /*expected_hr=*/S_OK, /*start=*/6, /*end=*/7,
1789       /*text=*/
1790       base::string16{ui::AXPlatformNodeBase::kEmbeddedCharacter}.c_str());
1791 
1792   // L"before" <[obj]> [obj] L"after" L"tail"
1793   EXPECT_IA2_TEXT_BEFORE_OFFSET(body_accessible_com, 6, IA2_TEXT_BOUNDARY_CHAR,
1794                                 /*expected_hr=*/S_OK, /*start=*/5, /*end=*/6,
1795                                 /*text=*/L"e");
1796 
1797   // <[obj]>
1798   EXPECT_IA2_TEXT_BEFORE_OFFSET(menu_1_accessible_com, 0,
1799                                 IA2_TEXT_BOUNDARY_CHAR,
1800                                 /*expected_hr=*/S_FALSE, /*start=*/0, /*end=*/0,
1801                                 /*text=*/nullptr);
1802 
1803   // L"<>"
1804   EXPECT_IA2_TEXT_BEFORE_OFFSET(menu_2_accessible_com, 0,
1805                                 IA2_TEXT_BOUNDARY_CHAR,
1806                                 /*expected_hr=*/E_INVALIDARG, /*start=*/0,
1807                                 /*end=*/0,
1808                                 /*text=*/nullptr);
1809 
1810   // L"befor<e>" [obj] <[obj]> L"after" L"tail"
1811   // TODO(crbug.com/1039528): This should not include multiple characters.
1812   {
1813     const std::array<base::char16, 3> pieces = {
1814         ui::AXPlatformNodeBase::kEmbeddedCharacter,
1815         ui::AXPlatformNodeBase::kEmbeddedCharacter, L'a'};
1816     const base::string16 expect(pieces.cbegin(), pieces.cend());
1817     EXPECT_IA2_TEXT_AFTER_OFFSET(body_accessible_com, 5, IA2_TEXT_BOUNDARY_CHAR,
1818                                  /*expected_hr=*/S_OK, /*start=*/6, /*end=*/9,
1819                                  /*text=*/expect.c_str());
1820   }
1821 
1822   // L"before" <[obj]> [obj] L"after" L"tail"
1823   // TODO(crbug.com/1039528): This should probably not skip over L"a"
1824   EXPECT_IA2_TEXT_AFTER_OFFSET(body_accessible_com, 6, IA2_TEXT_BOUNDARY_CHAR,
1825                                /*expected_hr=*/S_OK, /*start=*/9, /*end=*/10,
1826                                /*text=*/L"f");
1827 
1828   // <[obj]>
1829   EXPECT_IA2_TEXT_AFTER_OFFSET(menu_1_accessible_com, 0, IA2_TEXT_BOUNDARY_CHAR,
1830                                /*expected_hr=*/S_FALSE, /*start=*/0, /*end=*/0,
1831                                /*text=*/nullptr);
1832 
1833   // L"<>"
1834   EXPECT_IA2_TEXT_AFTER_OFFSET(menu_2_accessible_com, 0, IA2_TEXT_BOUNDARY_CHAR,
1835                                /*expected_hr=*/E_INVALIDARG, /*start=*/0,
1836                                /*end=*/0,
1837                                /*text=*/nullptr);
1838 }
1839 
TEST_F(BrowserAccessibilityWinTest,TestCaretAndSelectionInSimpleFields)1840 TEST_F(BrowserAccessibilityWinTest, TestCaretAndSelectionInSimpleFields) {
1841   ui::AXNodeData root;
1842   root.id = 1;
1843   root.role = ax::mojom::Role::kRootWebArea;
1844   root.AddState(ax::mojom::State::kFocusable);
1845 
1846   ui::AXNodeData combo_box;
1847   combo_box.id = 2;
1848   combo_box.role = ax::mojom::Role::kTextFieldWithComboBox;
1849   combo_box.AddState(ax::mojom::State::kEditable);
1850   combo_box.AddState(ax::mojom::State::kFocusable);
1851   combo_box.SetValue("Test1");
1852   // Place the caret between 't' and 'e'.
1853   combo_box.AddIntAttribute(ax::mojom::IntAttribute::kTextSelStart, 1);
1854   combo_box.AddIntAttribute(ax::mojom::IntAttribute::kTextSelEnd, 1);
1855 
1856   ui::AXNodeData text_field;
1857   text_field.id = 3;
1858   text_field.role = ax::mojom::Role::kTextField;
1859   text_field.AddState(ax::mojom::State::kEditable);
1860   text_field.AddState(ax::mojom::State::kFocusable);
1861   text_field.SetValue("Test2");
1862   // Select the letter 'e'.
1863   text_field.AddIntAttribute(ax::mojom::IntAttribute::kTextSelStart, 1);
1864   text_field.AddIntAttribute(ax::mojom::IntAttribute::kTextSelEnd, 2);
1865 
1866   root.child_ids.push_back(2);
1867   root.child_ids.push_back(3);
1868 
1869   std::unique_ptr<BrowserAccessibilityManager> manager(
1870       BrowserAccessibilityManager::Create(
1871           MakeAXTreeUpdate(root, combo_box, text_field),
1872           test_browser_accessibility_delegate_.get()));
1873 
1874   ASSERT_NE(nullptr, manager->GetRoot());
1875   BrowserAccessibilityWin* root_accessible =
1876       ToBrowserAccessibilityWin(manager->GetRoot());
1877   ASSERT_NE(nullptr, root_accessible);
1878   ASSERT_EQ(2U, root_accessible->PlatformChildCount());
1879 
1880   BrowserAccessibilityWin* combo_box_accessible =
1881       ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(0));
1882   ASSERT_NE(nullptr, combo_box_accessible);
1883   manager->SetFocusLocallyForTesting(combo_box_accessible);
1884   ASSERT_EQ(combo_box_accessible,
1885             ToBrowserAccessibilityWin(manager->GetFocus()));
1886   BrowserAccessibilityWin* text_field_accessible =
1887       ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(1));
1888   ASSERT_NE(nullptr, text_field_accessible);
1889 
1890   // -2 is never a valid offset.
1891   LONG caret_offset = -2;
1892   LONG n_selections = -2;
1893   LONG selection_start = -2;
1894   LONG selection_end = -2;
1895 
1896   // Test get_caretOffset.
1897   HRESULT hr = combo_box_accessible->GetCOM()->get_caretOffset(&caret_offset);
1898   EXPECT_EQ(S_OK, hr);
1899   EXPECT_EQ(1, caret_offset);
1900   // The caret should be at the end of the selection.
1901   hr = text_field_accessible->GetCOM()->get_caretOffset(&caret_offset);
1902   EXPECT_EQ(S_OK, hr);
1903   EXPECT_EQ(2, caret_offset);
1904 
1905   // Move the focus to the text field.
1906   manager->SetFocusLocallyForTesting(text_field_accessible);
1907   ASSERT_EQ(text_field_accessible,
1908             ToBrowserAccessibilityWin(manager->GetFocus()));
1909 
1910   // The caret should not have moved.
1911   hr = text_field_accessible->GetCOM()->get_caretOffset(&caret_offset);
1912   EXPECT_EQ(S_OK, hr);
1913   EXPECT_EQ(2, caret_offset);
1914 
1915   // Test get_nSelections.
1916   hr = combo_box_accessible->GetCOM()->get_nSelections(&n_selections);
1917   EXPECT_EQ(S_OK, hr);
1918   EXPECT_EQ(0, n_selections);
1919   hr = text_field_accessible->GetCOM()->get_nSelections(&n_selections);
1920   EXPECT_EQ(S_OK, hr);
1921   EXPECT_EQ(1, n_selections);
1922 
1923   // Test get_selection.
1924   hr = combo_box_accessible->GetCOM()->get_selection(
1925       0L /* selection_index */, &selection_start, &selection_end);
1926   EXPECT_EQ(E_INVALIDARG, hr);  // No selections available.
1927   hr = text_field_accessible->GetCOM()->get_selection(
1928       0L /* selection_index */, &selection_start, &selection_end);
1929   EXPECT_EQ(S_OK, hr);
1930   EXPECT_EQ(1, selection_start);
1931   EXPECT_EQ(2, selection_end);
1932 
1933   manager.reset();
1934 }
1935 
TEST_F(BrowserAccessibilityWinTest,TestCaretInContentEditables)1936 TEST_F(BrowserAccessibilityWinTest, TestCaretInContentEditables) {
1937   ui::AXNodeData root;
1938   root.id = 1;
1939   root.role = ax::mojom::Role::kRootWebArea;
1940   root.AddState(ax::mojom::State::kFocusable);
1941 
1942   ui::AXNodeData div_editable;
1943   div_editable.id = 2;
1944   div_editable.role = ax::mojom::Role::kGenericContainer;
1945   div_editable.AddState(ax::mojom::State::kEditable);
1946   div_editable.AddState(ax::mojom::State::kFocusable);
1947 
1948   ui::AXNodeData text;
1949   text.id = 3;
1950   text.role = ax::mojom::Role::kStaticText;
1951   text.AddState(ax::mojom::State::kEditable);
1952   text.SetName("Click ");
1953 
1954   ui::AXNodeData link;
1955   link.id = 4;
1956   link.role = ax::mojom::Role::kLink;
1957   link.AddState(ax::mojom::State::kEditable);
1958   link.AddState(ax::mojom::State::kFocusable);
1959   link.AddState(ax::mojom::State::kLinked);
1960   link.SetName("here");
1961 
1962   ui::AXNodeData link_text;
1963   link_text.id = 5;
1964   link_text.role = ax::mojom::Role::kStaticText;
1965   link_text.AddState(ax::mojom::State::kEditable);
1966   link_text.AddState(ax::mojom::State::kFocusable);
1967   link_text.AddState(ax::mojom::State::kLinked);
1968   link_text.SetName("here");
1969 
1970   root.child_ids.push_back(2);
1971   div_editable.child_ids.push_back(3);
1972   div_editable.child_ids.push_back(4);
1973   link.child_ids.push_back(5);
1974 
1975   ui::AXTreeUpdate update =
1976       MakeAXTreeUpdate(root, div_editable, link, link_text, text);
1977 
1978   // Place the caret between 'h' and 'e'.
1979   update.has_tree_data = true;
1980   update.tree_data.sel_anchor_object_id = 5;
1981   update.tree_data.sel_anchor_offset = 1;
1982   update.tree_data.sel_focus_object_id = 5;
1983   update.tree_data.sel_focus_offset = 1;
1984 
1985   std::unique_ptr<BrowserAccessibilityManager> manager(
1986       BrowserAccessibilityManager::Create(
1987           update, test_browser_accessibility_delegate_.get()));
1988 
1989   ASSERT_NE(nullptr, manager->GetRoot());
1990   BrowserAccessibilityWin* root_accessible =
1991       ToBrowserAccessibilityWin(manager->GetRoot());
1992   ASSERT_NE(nullptr, root_accessible);
1993   ASSERT_EQ(1U, root_accessible->PlatformChildCount());
1994 
1995   BrowserAccessibilityWin* div_editable_accessible =
1996       ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(0));
1997   ASSERT_NE(nullptr, div_editable_accessible);
1998   ASSERT_EQ(2U, div_editable_accessible->PlatformChildCount());
1999 
2000   // -2 is never a valid offset.
2001   LONG caret_offset = -2;
2002   LONG n_selections = -2;
2003 
2004   // No selection should be present.
2005   HRESULT hr =
2006       div_editable_accessible->GetCOM()->get_nSelections(&n_selections);
2007   EXPECT_EQ(S_OK, hr);
2008   EXPECT_EQ(0, n_selections);
2009 
2010   // The caret should be on the embedded object character.
2011   hr = div_editable_accessible->GetCOM()->get_caretOffset(&caret_offset);
2012   EXPECT_EQ(S_OK, hr);
2013   EXPECT_EQ(6, caret_offset);
2014 
2015   // Move the focus to the content editable.
2016   manager->SetFocusLocallyForTesting(div_editable_accessible);
2017   ASSERT_EQ(div_editable_accessible,
2018             ToBrowserAccessibilityWin(manager->GetFocus()));
2019 
2020   BrowserAccessibilityWin* text_accessible =
2021       ToBrowserAccessibilityWin(div_editable_accessible->PlatformGetChild(0));
2022   ASSERT_NE(nullptr, text_accessible);
2023   BrowserAccessibilityWin* link_accessible =
2024       ToBrowserAccessibilityWin(div_editable_accessible->PlatformGetChild(1));
2025   ASSERT_NE(nullptr, link_accessible);
2026   ASSERT_EQ(1U, link_accessible->PlatformChildCount());
2027 
2028   BrowserAccessibilityWin* link_text_accessible =
2029       ToBrowserAccessibilityWin(link_accessible->PlatformGetChild(0));
2030   ASSERT_NE(nullptr, link_text_accessible);
2031 
2032   // The caret should not have moved.
2033   hr = div_editable_accessible->GetCOM()->get_nSelections(&n_selections);
2034   EXPECT_EQ(S_OK, hr);
2035   EXPECT_EQ(0, n_selections);
2036   hr = div_editable_accessible->GetCOM()->get_caretOffset(&caret_offset);
2037   EXPECT_EQ(S_OK, hr);
2038   EXPECT_EQ(6, caret_offset);
2039 
2040   hr = link_accessible->GetCOM()->get_nSelections(&n_selections);
2041   EXPECT_EQ(S_OK, hr);
2042   EXPECT_EQ(0, n_selections);
2043   hr = link_text_accessible->GetCOM()->get_nSelections(&n_selections);
2044   EXPECT_EQ(S_OK, hr);
2045   EXPECT_EQ(0, n_selections);
2046 
2047   hr = link_accessible->GetCOM()->get_caretOffset(&caret_offset);
2048   EXPECT_EQ(S_OK, hr);
2049   EXPECT_EQ(1, caret_offset);
2050   hr = link_text_accessible->GetCOM()->get_caretOffset(&caret_offset);
2051   EXPECT_EQ(S_OK, hr);
2052   EXPECT_EQ(1, caret_offset);
2053 
2054   manager.reset();
2055 }
2056 
TEST_F(BrowserAccessibilityWinTest,TestSelectionInContentEditables)2057 TEST_F(BrowserAccessibilityWinTest, TestSelectionInContentEditables) {
2058   ui::AXNodeData root;
2059   root.id = 1;
2060   root.role = ax::mojom::Role::kRootWebArea;
2061   root.AddState(ax::mojom::State::kFocusable);
2062 
2063   ui::AXNodeData div_editable;
2064   div_editable.id = 2;
2065   div_editable.role = ax::mojom::Role::kGenericContainer;
2066   div_editable.AddState(ax::mojom::State::kFocusable);
2067   div_editable.AddState(ax::mojom::State::kEditable);
2068 
2069   ui::AXNodeData text;
2070   text.id = 3;
2071   text.role = ax::mojom::Role::kStaticText;
2072   text.AddState(ax::mojom::State::kFocusable);
2073   text.AddState(ax::mojom::State::kEditable);
2074   text.SetName("Click ");
2075 
2076   ui::AXNodeData link;
2077   link.id = 4;
2078   link.role = ax::mojom::Role::kLink;
2079   link.AddState(ax::mojom::State::kFocusable);
2080   link.AddState(ax::mojom::State::kEditable);
2081   link.AddState(ax::mojom::State::kLinked);
2082   link.SetName("here");
2083 
2084   ui::AXNodeData link_text;
2085   link_text.id = 5;
2086   link_text.role = ax::mojom::Role::kStaticText;
2087   link_text.AddState(ax::mojom::State::kFocusable);
2088   link_text.AddState(ax::mojom::State::kEditable);
2089   link_text.AddState(ax::mojom::State::kLinked);
2090   link_text.SetName("here");
2091 
2092   root.child_ids.push_back(2);
2093   div_editable.child_ids.push_back(3);
2094   div_editable.child_ids.push_back(4);
2095   link.child_ids.push_back(5);
2096 
2097   ui::AXTreeUpdate update =
2098       MakeAXTreeUpdate(root, div_editable, link, link_text, text);
2099 
2100   // Select the following part of the text: "lick here".
2101   update.has_tree_data = true;
2102   update.tree_data.sel_anchor_object_id = 3;
2103   update.tree_data.sel_anchor_offset = 1;
2104   update.tree_data.sel_focus_object_id = 5;
2105   update.tree_data.sel_focus_offset = 4;
2106 
2107   std::unique_ptr<BrowserAccessibilityManager> manager(
2108       BrowserAccessibilityManager::Create(
2109           update, test_browser_accessibility_delegate_.get()));
2110 
2111   ASSERT_NE(nullptr, manager->GetRoot());
2112   BrowserAccessibilityWin* root_accessible =
2113       ToBrowserAccessibilityWin(manager->GetRoot());
2114   ASSERT_NE(nullptr, root_accessible);
2115   ASSERT_EQ(1U, root_accessible->PlatformChildCount());
2116 
2117   BrowserAccessibilityWin* div_editable_accessible =
2118       ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(0));
2119   ASSERT_NE(nullptr, div_editable_accessible);
2120   ASSERT_EQ(2U, div_editable_accessible->PlatformChildCount());
2121 
2122   // -2 is never a valid offset.
2123   LONG caret_offset = -2;
2124   LONG n_selections = -2;
2125   LONG selection_start = -2;
2126   LONG selection_end = -2;
2127 
2128   BrowserAccessibilityWin* text_accessible =
2129       ToBrowserAccessibilityWin(div_editable_accessible->PlatformGetChild(0));
2130   ASSERT_NE(nullptr, text_accessible);
2131   BrowserAccessibilityWin* link_accessible =
2132       ToBrowserAccessibilityWin(div_editable_accessible->PlatformGetChild(1));
2133   ASSERT_NE(nullptr, link_accessible);
2134   ASSERT_EQ(1U, link_accessible->PlatformChildCount());
2135 
2136   BrowserAccessibilityWin* link_text_accessible =
2137       ToBrowserAccessibilityWin(link_accessible->PlatformGetChild(0));
2138   ASSERT_NE(nullptr, link_text_accessible);
2139 
2140   // get_nSelections should work on all objects.
2141   HRESULT hr =
2142       div_editable_accessible->GetCOM()->get_nSelections(&n_selections);
2143   EXPECT_EQ(S_OK, hr);
2144   EXPECT_EQ(1, n_selections);
2145   hr = text_accessible->GetCOM()->get_nSelections(&n_selections);
2146   EXPECT_EQ(S_OK, hr);
2147   EXPECT_EQ(1, n_selections);
2148   hr = link_accessible->GetCOM()->get_nSelections(&n_selections);
2149   EXPECT_EQ(S_OK, hr);
2150   EXPECT_EQ(1, n_selections);
2151   hr = link_text_accessible->GetCOM()->get_nSelections(&n_selections);
2152   EXPECT_EQ(S_OK, hr);
2153   EXPECT_EQ(1, n_selections);
2154 
2155   // get_selection should be unaffected by focus placement.
2156   hr = div_editable_accessible->GetCOM()->get_selection(
2157       0L /* selection_index */, &selection_start, &selection_end);
2158   EXPECT_EQ(S_OK, hr);
2159   EXPECT_EQ(1, selection_start);
2160   // selection_end should be after embedded object character.
2161   EXPECT_EQ(7, selection_end);
2162 
2163   hr = text_accessible->GetCOM()->get_selection(
2164       0L /* selection_index */, &selection_start, &selection_end);
2165   EXPECT_EQ(S_OK, hr);
2166   EXPECT_EQ(1, selection_start);
2167   // No embedded character on this object, only the first part of the text.
2168   EXPECT_EQ(6, selection_end);
2169   hr = link_accessible->GetCOM()->get_selection(
2170       0L /* selection_index */, &selection_start, &selection_end);
2171   EXPECT_EQ(S_OK, hr);
2172   EXPECT_EQ(0, selection_start);
2173   EXPECT_EQ(4, selection_end);
2174   hr = link_text_accessible->GetCOM()->get_selection(
2175       0L /* selection_index */, &selection_start, &selection_end);
2176   EXPECT_EQ(S_OK, hr);
2177   EXPECT_EQ(0, selection_start);
2178   EXPECT_EQ(4, selection_end);
2179 
2180   // The caret should be at the focus (the end) of the selection.
2181   hr = div_editable_accessible->GetCOM()->get_caretOffset(&caret_offset);
2182   EXPECT_EQ(S_OK, hr);
2183   EXPECT_EQ(7, caret_offset);
2184 
2185   // Move the focus to the content editable.
2186   manager->SetFocusLocallyForTesting(div_editable_accessible);
2187   ASSERT_EQ(div_editable_accessible,
2188             ToBrowserAccessibilityWin(manager->GetFocus()));
2189 
2190   // The caret should not have moved.
2191   hr = div_editable_accessible->GetCOM()->get_caretOffset(&caret_offset);
2192   EXPECT_EQ(S_OK, hr);
2193   EXPECT_EQ(7, caret_offset);
2194 
2195   // The caret offset should reflect the position of the selection's focus in
2196   // any given object.
2197   hr = link_accessible->GetCOM()->get_caretOffset(&caret_offset);
2198   EXPECT_EQ(S_OK, hr);
2199   EXPECT_EQ(4, caret_offset);
2200   hr = link_text_accessible->GetCOM()->get_caretOffset(&caret_offset);
2201   EXPECT_EQ(S_OK, hr);
2202   EXPECT_EQ(4, caret_offset);
2203 
2204   hr = div_editable_accessible->GetCOM()->get_selection(
2205       0L /* selection_index */, &selection_start, &selection_end);
2206   EXPECT_EQ(S_OK, hr);
2207   EXPECT_EQ(1, selection_start);
2208   EXPECT_EQ(7, selection_end);
2209 
2210   manager.reset();
2211 }
2212 
TEST_F(BrowserAccessibilityWinTest,TestIAccessibleHyperlink)2213 TEST_F(BrowserAccessibilityWinTest, TestIAccessibleHyperlink) {
2214   ui::AXNodeData root;
2215   root.id = 1;
2216   root.role = ax::mojom::Role::kRootWebArea;
2217   root.AddState(ax::mojom::State::kFocusable);
2218 
2219   ui::AXNodeData div;
2220   div.id = 2;
2221   div.role = ax::mojom::Role::kGenericContainer;
2222   div.AddState(ax::mojom::State::kFocusable);
2223 
2224   ui::AXNodeData text;
2225   text.id = 3;
2226   text.role = ax::mojom::Role::kStaticText;
2227   text.SetName("Click ");
2228 
2229   ui::AXNodeData link;
2230   link.id = 4;
2231   link.role = ax::mojom::Role::kLink;
2232   link.AddState(ax::mojom::State::kFocusable);
2233   link.AddState(ax::mojom::State::kLinked);
2234   link.SetName("here");
2235   link.SetNameFrom(ax::mojom::NameFrom::kContents);
2236   link.AddStringAttribute(ax::mojom::StringAttribute::kUrl, "example.com");
2237 
2238   root.child_ids.push_back(div.id);
2239   div.child_ids = {text.id, link.id};
2240 
2241   std::unique_ptr<BrowserAccessibilityManager> manager(
2242       BrowserAccessibilityManager::Create(
2243           MakeAXTreeUpdate(root, div, link, text),
2244           test_browser_accessibility_delegate_.get()));
2245 
2246   ASSERT_NE(nullptr, manager->GetRoot());
2247   BrowserAccessibilityWin* root_accessible =
2248       ToBrowserAccessibilityWin(manager->GetRoot());
2249   ASSERT_NE(nullptr, root_accessible);
2250   ASSERT_EQ(1U, root_accessible->PlatformChildCount());
2251 
2252   BrowserAccessibilityWin* div_accessible =
2253       ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(0));
2254   ASSERT_NE(nullptr, div_accessible);
2255   ASSERT_EQ(2U, div_accessible->PlatformChildCount());
2256 
2257   BrowserAccessibilityWin* text_accessible =
2258       ToBrowserAccessibilityWin(div_accessible->PlatformGetChild(0));
2259   ASSERT_NE(nullptr, text_accessible);
2260   BrowserAccessibilityWin* link_accessible =
2261       ToBrowserAccessibilityWin(div_accessible->PlatformGetChild(1));
2262   ASSERT_NE(nullptr, link_accessible);
2263 
2264   // -1 is never a valid value.
2265   LONG n_actions = -1;
2266   LONG start_index = -1;
2267   LONG end_index = -1;
2268 
2269   Microsoft::WRL::ComPtr<IAccessibleHyperlink> hyperlink;
2270   base::win::ScopedVariant anchor;
2271   base::win::ScopedVariant anchor_target;
2272   base::win::ScopedBstr bstr;
2273 
2274   base::string16 div_hypertext(L"Click ");
2275   div_hypertext.push_back(BrowserAccessibilityComWin::kEmbeddedCharacter);
2276 
2277   // div_accessible and link_accessible are the only IA2 hyperlinks.
2278   EXPECT_HRESULT_FAILED(
2279       root_accessible->GetCOM()->QueryInterface(IID_PPV_ARGS(&hyperlink)));
2280   hyperlink.Reset();
2281   EXPECT_HRESULT_SUCCEEDED(
2282       div_accessible->GetCOM()->QueryInterface(IID_PPV_ARGS(&hyperlink)));
2283   hyperlink.Reset();
2284   EXPECT_HRESULT_FAILED(
2285       text_accessible->GetCOM()->QueryInterface(IID_PPV_ARGS(&hyperlink)));
2286   hyperlink.Reset();
2287   EXPECT_HRESULT_SUCCEEDED(
2288       link_accessible->GetCOM()->QueryInterface(IID_PPV_ARGS(&hyperlink)));
2289   hyperlink.Reset();
2290 
2291   EXPECT_HRESULT_SUCCEEDED(root_accessible->GetCOM()->nActions(&n_actions));
2292   EXPECT_EQ(0, n_actions);
2293   EXPECT_HRESULT_SUCCEEDED(div_accessible->GetCOM()->nActions(&n_actions));
2294   EXPECT_EQ(1, n_actions);
2295   EXPECT_HRESULT_SUCCEEDED(text_accessible->GetCOM()->nActions(&n_actions));
2296   EXPECT_EQ(0, n_actions);
2297   EXPECT_HRESULT_SUCCEEDED(link_accessible->GetCOM()->nActions(&n_actions));
2298   EXPECT_EQ(1, n_actions);
2299 
2300   EXPECT_HRESULT_FAILED(
2301       root_accessible->GetCOM()->get_anchor(0, anchor.Receive()));
2302   anchor.Reset();
2303   HRESULT hr = div_accessible->GetCOM()->get_anchor(0, anchor.Receive());
2304   EXPECT_EQ(S_OK, hr);
2305   EXPECT_EQ(VT_BSTR, anchor.type());
2306   bstr.Reset(V_BSTR(anchor.ptr()));
2307   EXPECT_STREQ(div_hypertext.c_str(), bstr.Get());
2308   bstr.Reset();
2309   anchor.Reset();
2310   EXPECT_HRESULT_FAILED(
2311       text_accessible->GetCOM()->get_anchor(0, anchor.Receive()));
2312   anchor.Reset();
2313   hr = link_accessible->GetCOM()->get_anchor(0, anchor.Receive());
2314   EXPECT_EQ(S_OK, hr);
2315   EXPECT_EQ(VT_BSTR, anchor.type());
2316   bstr.Reset(V_BSTR(anchor.ptr()));
2317   EXPECT_STREQ(L"here", bstr.Get());
2318   bstr.Reset();
2319   anchor.Reset();
2320   EXPECT_HRESULT_FAILED(
2321       div_accessible->GetCOM()->get_anchor(1, anchor.Receive()));
2322   anchor.Reset();
2323   EXPECT_HRESULT_FAILED(
2324       link_accessible->GetCOM()->get_anchor(1, anchor.Receive()));
2325   anchor.Reset();
2326 
2327   EXPECT_HRESULT_FAILED(
2328       root_accessible->GetCOM()->get_anchorTarget(0, anchor_target.Receive()));
2329   anchor_target.Reset();
2330   hr = div_accessible->GetCOM()->get_anchorTarget(0, anchor_target.Receive());
2331   EXPECT_EQ(S_FALSE, hr);
2332   EXPECT_EQ(VT_BSTR, anchor_target.type());
2333   bstr.Reset(V_BSTR(anchor_target.ptr()));
2334   // Target should be empty.
2335   EXPECT_STREQ(L"", bstr.Get());
2336   bstr.Reset();
2337   anchor_target.Reset();
2338   EXPECT_HRESULT_FAILED(
2339       text_accessible->GetCOM()->get_anchorTarget(0, anchor_target.Receive()));
2340   anchor_target.Reset();
2341   hr = link_accessible->GetCOM()->get_anchorTarget(0, anchor_target.Receive());
2342   EXPECT_EQ(S_OK, hr);
2343   EXPECT_EQ(VT_BSTR, anchor_target.type());
2344   bstr.Reset(V_BSTR(anchor_target.ptr()));
2345   EXPECT_STREQ(L"example.com", bstr.Get());
2346   bstr.Reset();
2347   anchor_target.Reset();
2348   EXPECT_HRESULT_FAILED(
2349       div_accessible->GetCOM()->get_anchorTarget(1, anchor_target.Receive()));
2350   anchor_target.Reset();
2351   EXPECT_HRESULT_FAILED(
2352       link_accessible->GetCOM()->get_anchorTarget(1, anchor_target.Receive()));
2353   anchor_target.Reset();
2354 
2355   EXPECT_HRESULT_FAILED(
2356       root_accessible->GetCOM()->get_startIndex(&start_index));
2357   EXPECT_HRESULT_SUCCEEDED(
2358       div_accessible->GetCOM()->get_startIndex(&start_index));
2359   EXPECT_EQ(0, start_index);
2360   EXPECT_HRESULT_FAILED(
2361       text_accessible->GetCOM()->get_startIndex(&start_index));
2362   EXPECT_HRESULT_SUCCEEDED(
2363       link_accessible->GetCOM()->get_startIndex(&start_index));
2364   EXPECT_EQ(6, start_index);
2365 
2366   EXPECT_HRESULT_FAILED(root_accessible->GetCOM()->get_endIndex(&end_index));
2367   EXPECT_HRESULT_SUCCEEDED(div_accessible->GetCOM()->get_endIndex(&end_index));
2368   EXPECT_EQ(1, end_index);
2369   EXPECT_HRESULT_FAILED(text_accessible->GetCOM()->get_endIndex(&end_index));
2370   EXPECT_HRESULT_SUCCEEDED(link_accessible->GetCOM()->get_endIndex(&end_index));
2371   EXPECT_EQ(7, end_index);
2372 }
2373 
TEST_F(BrowserAccessibilityWinTest,TestTextAttributesInContentEditables)2374 TEST_F(BrowserAccessibilityWinTest, TestTextAttributesInContentEditables) {
2375   ui::AXNodeData root;
2376   root.id = 1;
2377   root.role = ax::mojom::Role::kRootWebArea;
2378   root.AddState(ax::mojom::State::kFocusable);
2379 
2380   ui::AXNodeData div_editable;
2381   div_editable.id = 2;
2382   div_editable.role = ax::mojom::Role::kGenericContainer;
2383   div_editable.AddState(ax::mojom::State::kEditable);
2384   div_editable.AddState(ax::mojom::State::kFocusable);
2385   div_editable.AddStringAttribute(ax::mojom::StringAttribute::kFontFamily,
2386                                   "Helvetica");
2387 
2388   ui::AXNodeData text_before;
2389   text_before.id = 3;
2390   text_before.role = ax::mojom::Role::kStaticText;
2391   text_before.AddState(ax::mojom::State::kEditable);
2392   text_before.SetName("Before ");
2393   text_before.AddTextStyle(ax::mojom::TextStyle::kBold);
2394   text_before.AddTextStyle(ax::mojom::TextStyle::kItalic);
2395 
2396   ui::AXNodeData link;
2397   link.id = 4;
2398   link.role = ax::mojom::Role::kLink;
2399   link.AddState(ax::mojom::State::kEditable);
2400   link.AddState(ax::mojom::State::kFocusable);
2401   link.AddState(ax::mojom::State::kLinked);
2402   link.SetName("lnk");
2403   link.AddTextStyle(ax::mojom::TextStyle::kUnderline);
2404 
2405   ui::AXNodeData link_text;
2406   link_text.id = 5;
2407   link_text.role = ax::mojom::Role::kStaticText;
2408   link_text.AddState(ax::mojom::State::kEditable);
2409   link_text.AddState(ax::mojom::State::kFocusable);
2410   link_text.AddState(ax::mojom::State::kLinked);
2411   link_text.SetName("lnk");
2412   link_text.AddTextStyle(ax::mojom::TextStyle::kUnderline);
2413 
2414   // The name "lnk" is misspelled.
2415   std::vector<int32_t> marker_types{
2416       static_cast<int32_t>(ax::mojom::MarkerType::kSpelling)};
2417   std::vector<int32_t> marker_starts{0};
2418   std::vector<int32_t> marker_ends{3};
2419   link_text.AddIntListAttribute(ax::mojom::IntListAttribute::kMarkerTypes,
2420                                 marker_types);
2421   link_text.AddIntListAttribute(ax::mojom::IntListAttribute::kMarkerStarts,
2422                                 marker_starts);
2423   link_text.AddIntListAttribute(ax::mojom::IntListAttribute::kMarkerEnds,
2424                                 marker_ends);
2425 
2426   ui::AXNodeData text_after;
2427   text_after.id = 6;
2428   text_after.role = ax::mojom::Role::kStaticText;
2429   text_after.AddState(ax::mojom::State::kEditable);
2430   text_after.SetName(" after.");
2431   // Leave text style as normal.
2432 
2433   root.child_ids.push_back(div_editable.id);
2434   div_editable.child_ids.push_back(text_before.id);
2435   div_editable.child_ids.push_back(link.id);
2436   div_editable.child_ids.push_back(text_after.id);
2437   link.child_ids.push_back(link_text.id);
2438 
2439   ui::AXTreeUpdate update = MakeAXTreeUpdate(root, div_editable, text_before,
2440                                              link, link_text, text_after);
2441 
2442   std::unique_ptr<BrowserAccessibilityManager> manager(
2443       BrowserAccessibilityManager::Create(
2444           update, test_browser_accessibility_delegate_.get()));
2445 
2446   ASSERT_NE(nullptr, manager->GetRoot());
2447   BrowserAccessibilityWin* ax_root =
2448       ToBrowserAccessibilityWin(manager->GetRoot());
2449   ASSERT_NE(nullptr, ax_root);
2450   ASSERT_EQ(1U, ax_root->PlatformChildCount());
2451 
2452   BrowserAccessibilityWin* ax_div =
2453       ToBrowserAccessibilityWin(ax_root->PlatformGetChild(0));
2454   ASSERT_NE(nullptr, ax_div);
2455   ASSERT_EQ(3U, ax_div->PlatformChildCount());
2456 
2457   BrowserAccessibilityWin* ax_before =
2458       ToBrowserAccessibilityWin(ax_div->PlatformGetChild(0));
2459   ASSERT_NE(nullptr, ax_before);
2460   BrowserAccessibilityWin* ax_link =
2461       ToBrowserAccessibilityWin(ax_div->PlatformGetChild(1));
2462   ASSERT_NE(nullptr, ax_link);
2463   ASSERT_EQ(1U, ax_link->PlatformChildCount());
2464   BrowserAccessibilityWin* ax_after =
2465       ToBrowserAccessibilityWin(ax_div->PlatformGetChild(2));
2466   ASSERT_NE(nullptr, ax_after);
2467 
2468   BrowserAccessibilityWin* ax_link_text =
2469       ToBrowserAccessibilityWin(ax_link->PlatformGetChild(0));
2470   ASSERT_NE(nullptr, ax_link_text);
2471 
2472   HRESULT hr;
2473   LONG n_characters, start_offset, end_offset;
2474   base::win::ScopedBstr text_attributes;
2475 
2476   ASSERT_HRESULT_SUCCEEDED(ax_root->GetCOM()->get_nCharacters(&n_characters));
2477   ASSERT_EQ(1, n_characters);
2478   ASSERT_HRESULT_SUCCEEDED(ax_div->GetCOM()->get_nCharacters(&n_characters));
2479   ASSERT_EQ(15, n_characters);
2480 
2481   // Test the style of the root.
2482   hr = ax_root->GetCOM()->get_attributes(0, &start_offset, &end_offset,
2483                                          text_attributes.Receive());
2484   EXPECT_EQ(S_OK, hr);
2485   EXPECT_EQ(0, start_offset);
2486   EXPECT_EQ(1, end_offset);
2487   EXPECT_NE(
2488       base::string16::npos,
2489       base::string16(text_attributes.Get()).find(L"font-family:Helvetica"));
2490   text_attributes.Reset();
2491 
2492   // Test the style of text_before.
2493   for (LONG offset = 0; offset < 7; ++offset) {
2494     hr = ax_div->GetCOM()->get_attributes(0, &start_offset, &end_offset,
2495                                           text_attributes.Receive());
2496     EXPECT_EQ(S_OK, hr);
2497     EXPECT_EQ(0, start_offset);
2498     EXPECT_EQ(7, end_offset);
2499     base::string16 attributes(text_attributes.Get());
2500     EXPECT_NE(base::string16::npos, attributes.find(L"font-family:Helvetica"));
2501     EXPECT_NE(base::string16::npos, attributes.find(L"font-weight:bold"));
2502     EXPECT_NE(base::string16::npos, attributes.find(L"font-style:italic"));
2503     text_attributes.Reset();
2504   }
2505 
2506   // Test the style of the link.
2507   hr = ax_link->GetCOM()->get_attributes(0, &start_offset, &end_offset,
2508                                          text_attributes.Receive());
2509   EXPECT_EQ(S_OK, hr);
2510   EXPECT_EQ(0, start_offset);
2511   EXPECT_EQ(3, end_offset);
2512   EXPECT_NE(
2513       base::string16::npos,
2514       base::string16(text_attributes.Get()).find(L"font-family:Helvetica"));
2515   EXPECT_EQ(base::string16::npos,
2516             base::string16(text_attributes.Get()).find(L"font-weight:"));
2517   EXPECT_EQ(base::string16::npos,
2518             base::string16(text_attributes.Get()).find(L"font-style:"));
2519   EXPECT_NE(base::string16::npos, base::string16(text_attributes.Get())
2520                                       .find(L"text-underline-style:solid"));
2521   EXPECT_EQ(
2522       base::string16::npos,
2523       base::string16(text_attributes.Get()).find(L"text-underline-type:"));
2524   // For compatibility with Firefox, spelling attributes should also be
2525   // propagated to the parent of static text leaves.
2526   EXPECT_NE(base::string16::npos,
2527             base::string16(text_attributes.Get()).find(L"invalid:spelling"));
2528   text_attributes.Reset();
2529 
2530   hr = ax_link_text->GetCOM()->get_attributes(2, &start_offset, &end_offset,
2531                                               text_attributes.Receive());
2532   EXPECT_EQ(S_OK, hr);
2533   EXPECT_EQ(0, start_offset);
2534   EXPECT_EQ(3, end_offset);
2535   EXPECT_NE(
2536       base::string16::npos,
2537       base::string16(text_attributes.Get()).find(L"font-family:Helvetica"));
2538   EXPECT_EQ(base::string16::npos,
2539             base::string16(text_attributes.Get()).find(L"font-weight:"));
2540   EXPECT_EQ(base::string16::npos,
2541             base::string16(text_attributes.Get()).find(L"font-style:"));
2542   EXPECT_NE(base::string16::npos, base::string16(text_attributes.Get())
2543                                       .find(L"text-underline-style:solid"));
2544   EXPECT_EQ(
2545       base::string16::npos,
2546       base::string16(text_attributes.Get()).find(L"text-underline-type:"));
2547   EXPECT_NE(base::string16::npos,
2548             base::string16(text_attributes.Get()).find(L"invalid:spelling"));
2549   text_attributes.Reset();
2550 
2551   // Test the style of text_after.
2552   for (LONG offset = 8; offset < 15; ++offset) {
2553     hr = ax_div->GetCOM()->get_attributes(offset, &start_offset, &end_offset,
2554                                           text_attributes.Receive());
2555     EXPECT_EQ(S_OK, hr);
2556     EXPECT_EQ(8, start_offset);
2557     EXPECT_EQ(15, end_offset);
2558     base::string16 attributes(text_attributes.Get());
2559     EXPECT_NE(base::string16::npos, attributes.find(L"font-family:Helvetica"));
2560     EXPECT_EQ(base::string16::npos, attributes.find(L"font-weight:"));
2561     EXPECT_EQ(base::string16::npos, attributes.find(L"font-style:"));
2562     EXPECT_EQ(base::string16::npos, base::string16(text_attributes.Get())
2563                                         .find(L"text-underline-style:solid"));
2564     EXPECT_EQ(
2565         base::string16::npos,
2566         base::string16(text_attributes.Get()).find(L"text-underline-type:"));
2567     EXPECT_EQ(base::string16::npos, attributes.find(L"invalid:spelling"));
2568     text_attributes.Reset();
2569   }
2570 
2571   // Test the style of the static text nodes.
2572   hr = ax_before->GetCOM()->get_attributes(6, &start_offset, &end_offset,
2573                                            text_attributes.Receive());
2574   EXPECT_EQ(S_OK, hr);
2575   EXPECT_EQ(0, start_offset);
2576   EXPECT_EQ(7, end_offset);
2577   EXPECT_NE(
2578       base::string16::npos,
2579       base::string16(text_attributes.Get()).find(L"font-family:Helvetica"));
2580   EXPECT_NE(base::string16::npos,
2581             base::string16(text_attributes.Get()).find(L"font-weight:bold"));
2582   EXPECT_NE(base::string16::npos,
2583             base::string16(text_attributes.Get()).find(L"font-style:italic"));
2584   EXPECT_EQ(base::string16::npos,
2585             base::string16(text_attributes.Get()).find(L"invalid:spelling"));
2586   text_attributes.Reset();
2587 
2588   hr = ax_after->GetCOM()->get_attributes(6, &start_offset, &end_offset,
2589                                           text_attributes.Receive());
2590   EXPECT_EQ(S_OK, hr);
2591   EXPECT_EQ(0, start_offset);
2592   EXPECT_EQ(7, end_offset);
2593   EXPECT_NE(
2594       base::string16::npos,
2595       base::string16(text_attributes.Get()).find(L"font-family:Helvetica"));
2596   EXPECT_EQ(base::string16::npos,
2597             base::string16(text_attributes.Get()).find(L"font-weight:"));
2598   EXPECT_EQ(base::string16::npos,
2599             base::string16(text_attributes.Get()).find(L"font-style:"));
2600   EXPECT_EQ(
2601       base::string16::npos,
2602       base::string16(text_attributes.Get()).find(L"text-underline-style:"));
2603   EXPECT_EQ(
2604       base::string16::npos,
2605       base::string16(text_attributes.Get()).find(L"text-underline-type:"));
2606   EXPECT_EQ(base::string16::npos,
2607             base::string16(text_attributes.Get()).find(L"invalid:spelling"));
2608   text_attributes.Reset();
2609 
2610   manager.reset();
2611 }
2612 
TEST_F(BrowserAccessibilityWinTest,TestExistingMisspellingsInSimpleTextFields)2613 TEST_F(BrowserAccessibilityWinTest,
2614        TestExistingMisspellingsInSimpleTextFields) {
2615   std::string value1("Testing .");
2616   // The word "helo" is misspelled.
2617   std::string value2("Helo there.");
2618 
2619   LONG value1_length = static_cast<LONG>(value1.length());
2620   LONG value2_length = static_cast<LONG>(value2.length());
2621   LONG combo_box_value_length = value1_length + value2_length;
2622 
2623   ui::AXNodeData root;
2624   root.id = 1;
2625   root.role = ax::mojom::Role::kRootWebArea;
2626   root.AddState(ax::mojom::State::kFocusable);
2627 
2628   ui::AXNodeData combo_box;
2629   combo_box.id = 2;
2630   combo_box.role = ax::mojom::Role::kTextFieldWithComboBox;
2631   combo_box.AddState(ax::mojom::State::kEditable);
2632   combo_box.AddState(ax::mojom::State::kFocusable);
2633   combo_box.SetValue(value1 + value2);
2634 
2635   ui::AXNodeData combo_box_div;
2636   combo_box_div.id = 3;
2637   combo_box_div.role = ax::mojom::Role::kGenericContainer;
2638   combo_box_div.AddState(ax::mojom::State::kEditable);
2639 
2640   ui::AXNodeData static_text1;
2641   static_text1.id = 4;
2642   static_text1.role = ax::mojom::Role::kStaticText;
2643   static_text1.AddState(ax::mojom::State::kEditable);
2644   static_text1.SetName(value1);
2645 
2646   ui::AXNodeData static_text2;
2647   static_text2.id = 5;
2648   static_text2.role = ax::mojom::Role::kStaticText;
2649   static_text2.AddState(ax::mojom::State::kEditable);
2650   static_text2.SetName(value2);
2651 
2652   std::vector<int32_t> marker_types;
2653   marker_types.push_back(
2654       static_cast<int32_t>(ax::mojom::MarkerType::kSpelling));
2655   std::vector<int32_t> marker_starts;
2656   marker_starts.push_back(0);
2657   std::vector<int32_t> marker_ends;
2658   marker_ends.push_back(4);
2659   static_text2.AddIntListAttribute(ax::mojom::IntListAttribute::kMarkerTypes,
2660                                    marker_types);
2661   static_text2.AddIntListAttribute(ax::mojom::IntListAttribute::kMarkerStarts,
2662                                    marker_starts);
2663   static_text2.AddIntListAttribute(ax::mojom::IntListAttribute::kMarkerEnds,
2664                                    marker_ends);
2665 
2666   root.child_ids.push_back(combo_box.id);
2667   combo_box.child_ids.push_back(combo_box_div.id);
2668   combo_box_div.child_ids.push_back(static_text1.id);
2669   combo_box_div.child_ids.push_back(static_text2.id);
2670 
2671   std::unique_ptr<BrowserAccessibilityManager> manager(
2672       BrowserAccessibilityManager::Create(
2673           MakeAXTreeUpdate(root, combo_box, combo_box_div, static_text1,
2674                            static_text2),
2675           test_browser_accessibility_delegate_.get()));
2676 
2677   ASSERT_NE(nullptr, manager->GetRoot());
2678   BrowserAccessibilityWin* ax_root =
2679       ToBrowserAccessibilityWin(manager->GetRoot());
2680   ASSERT_NE(nullptr, ax_root);
2681   ASSERT_EQ(1U, ax_root->PlatformChildCount());
2682 
2683   BrowserAccessibilityWin* ax_combo_box =
2684       ToBrowserAccessibilityWin(ax_root->PlatformGetChild(0));
2685   ASSERT_NE(nullptr, ax_combo_box);
2686 
2687   HRESULT hr;
2688   LONG start_offset, end_offset;
2689   base::win::ScopedBstr text_attributes;
2690 
2691   // Ensure that the first part of the value is not marked misspelled.
2692   for (LONG offset = 0; offset < value1_length; ++offset) {
2693     hr = ax_combo_box->GetCOM()->get_attributes(
2694         offset, &start_offset, &end_offset, text_attributes.Receive());
2695     EXPECT_TRUE(base::string16(text_attributes.Get()).empty());
2696     EXPECT_EQ(0, start_offset);
2697     EXPECT_EQ(value1_length, end_offset);
2698     text_attributes.Reset();
2699   }
2700 
2701   // Ensure that "helo" is marked misspelled.
2702   for (LONG offset = value1_length; offset < value1_length + 4; ++offset) {
2703     hr = ax_combo_box->GetCOM()->get_attributes(
2704         offset, &start_offset, &end_offset, text_attributes.Receive());
2705     EXPECT_EQ(S_OK, hr);
2706     EXPECT_EQ(value1_length, start_offset);
2707     EXPECT_EQ(value1_length + 4, end_offset);
2708     EXPECT_NE(base::string16::npos,
2709               base::string16(text_attributes.Get()).find(L"invalid:spelling"));
2710     text_attributes.Reset();
2711   }
2712 
2713   // Ensure that the last part of the value is not marked misspelled.
2714   for (LONG offset = value1_length + 4; offset < combo_box_value_length;
2715        ++offset) {
2716     hr = ax_combo_box->GetCOM()->get_attributes(
2717         offset, &start_offset, &end_offset, text_attributes.Receive());
2718     EXPECT_TRUE(base::string16(text_attributes.Get()).empty());
2719     EXPECT_EQ(value1_length + 4, start_offset);
2720     EXPECT_EQ(combo_box_value_length, end_offset);
2721     text_attributes.Reset();
2722   }
2723 
2724   manager.reset();
2725 }
2726 
TEST_F(BrowserAccessibilityWinTest,TestNewMisspellingsInSimpleTextFields)2727 TEST_F(BrowserAccessibilityWinTest, TestNewMisspellingsInSimpleTextFields) {
2728   std::string value1("Testing .");
2729   // The word "helo" is misspelled.
2730   std::string value2("Helo there.");
2731 
2732   LONG value1_length = static_cast<LONG>(value1.length());
2733   LONG value2_length = static_cast<LONG>(value2.length());
2734   LONG combo_box_value_length = value1_length + value2_length;
2735 
2736   ui::AXNodeData root;
2737   root.id = 1;
2738   root.role = ax::mojom::Role::kRootWebArea;
2739   root.AddState(ax::mojom::State::kFocusable);
2740 
2741   ui::AXNodeData combo_box;
2742   combo_box.id = 2;
2743   combo_box.role = ax::mojom::Role::kTextFieldWithComboBox;
2744   combo_box.AddState(ax::mojom::State::kEditable);
2745   combo_box.AddState(ax::mojom::State::kFocusable);
2746   combo_box.SetValue(value1 + value2);
2747 
2748   ui::AXNodeData combo_box_div;
2749   combo_box_div.id = 3;
2750   combo_box_div.role = ax::mojom::Role::kGenericContainer;
2751   combo_box_div.AddState(ax::mojom::State::kEditable);
2752 
2753   ui::AXNodeData static_text1;
2754   static_text1.id = 4;
2755   static_text1.role = ax::mojom::Role::kStaticText;
2756   static_text1.AddState(ax::mojom::State::kEditable);
2757   static_text1.SetName(value1);
2758 
2759   ui::AXNodeData static_text2;
2760   static_text2.id = 5;
2761   static_text2.role = ax::mojom::Role::kStaticText;
2762   static_text2.AddState(ax::mojom::State::kEditable);
2763   static_text2.SetName(value2);
2764 
2765   root.child_ids.push_back(combo_box.id);
2766   combo_box.child_ids.push_back(combo_box_div.id);
2767   combo_box_div.child_ids.push_back(static_text1.id);
2768   combo_box_div.child_ids.push_back(static_text2.id);
2769 
2770   std::unique_ptr<BrowserAccessibilityManager> manager(
2771       BrowserAccessibilityManager::Create(
2772           MakeAXTreeUpdate(root, combo_box, combo_box_div, static_text1,
2773                            static_text2),
2774           test_browser_accessibility_delegate_.get()));
2775 
2776   ASSERT_NE(nullptr, manager->GetRoot());
2777   BrowserAccessibilityWin* ax_root =
2778       ToBrowserAccessibilityWin(manager->GetRoot());
2779   ASSERT_NE(nullptr, ax_root);
2780   ASSERT_EQ(1U, ax_root->PlatformChildCount());
2781 
2782   BrowserAccessibilityWin* ax_combo_box =
2783       ToBrowserAccessibilityWin(ax_root->PlatformGetChild(0));
2784   ASSERT_NE(nullptr, ax_combo_box);
2785 
2786   HRESULT hr;
2787   LONG start_offset, end_offset;
2788   base::win::ScopedBstr text_attributes;
2789 
2790   // Ensure that nothing is marked misspelled.
2791   for (LONG offset = 0; offset < combo_box_value_length; ++offset) {
2792     hr = ax_combo_box->GetCOM()->get_attributes(
2793         offset, &start_offset, &end_offset, text_attributes.Receive());
2794     EXPECT_TRUE(base::string16(text_attributes.Get()).empty());
2795     EXPECT_EQ(0, start_offset);
2796     EXPECT_EQ(combo_box_value_length, end_offset);
2797     text_attributes.Reset();
2798   }
2799 
2800   // Add the spelling markers on "helo".
2801   std::vector<int32_t> marker_types{
2802       static_cast<int32_t>(ax::mojom::MarkerType::kSpelling)};
2803   std::vector<int32_t> marker_starts{0};
2804   std::vector<int32_t> marker_ends{4};
2805   static_text2.AddIntListAttribute(ax::mojom::IntListAttribute::kMarkerTypes,
2806                                    marker_types);
2807   static_text2.AddIntListAttribute(ax::mojom::IntListAttribute::kMarkerStarts,
2808                                    marker_starts);
2809   static_text2.AddIntListAttribute(ax::mojom::IntListAttribute::kMarkerEnds,
2810                                    marker_ends);
2811   ui::AXTree* tree = const_cast<ui::AXTree*>(manager->ax_tree());
2812   ASSERT_NE(nullptr, tree);
2813   ASSERT_TRUE(tree->Unserialize(MakeAXTreeUpdate(static_text2)));
2814 
2815   // Ensure that value1 is still not marked misspelled.
2816   for (LONG offset = 0; offset < value1_length; ++offset) {
2817     hr = ax_combo_box->GetCOM()->get_attributes(
2818         offset, &start_offset, &end_offset, text_attributes.Receive());
2819     EXPECT_TRUE(base::string16(text_attributes.Get()).empty());
2820     EXPECT_EQ(0, start_offset);
2821     EXPECT_EQ(value1_length, end_offset);
2822     text_attributes.Reset();
2823   }
2824 
2825   // Ensure that "helo" is now marked misspelled.
2826   for (LONG offset = value1_length; offset < value1_length + 4; ++offset) {
2827     hr = ax_combo_box->GetCOM()->get_attributes(
2828         offset, &start_offset, &end_offset, text_attributes.Receive());
2829     EXPECT_EQ(S_OK, hr);
2830     EXPECT_EQ(value1_length, start_offset);
2831     EXPECT_EQ(value1_length + 4, end_offset);
2832     EXPECT_NE(base::string16::npos,
2833               base::string16(text_attributes.Get()).find(L"invalid:spelling"));
2834     text_attributes.Reset();
2835   }
2836 
2837   // Ensure that the last part of the value is not marked misspelled.
2838   for (LONG offset = value1_length + 4; offset < combo_box_value_length;
2839        ++offset) {
2840     hr = ax_combo_box->GetCOM()->get_attributes(
2841         offset, &start_offset, &end_offset, text_attributes.Receive());
2842     EXPECT_TRUE(base::string16(text_attributes.Get()).empty());
2843     EXPECT_EQ(value1_length + 4, start_offset);
2844     EXPECT_EQ(combo_box_value_length, end_offset);
2845     text_attributes.Reset();
2846   }
2847 
2848   manager.reset();
2849 }
2850 
TEST_F(BrowserAccessibilityWinTest,TestDeepestFirstLastChild)2851 TEST_F(BrowserAccessibilityWinTest, TestDeepestFirstLastChild) {
2852   ui::AXNodeData root;
2853   root.id = 1;
2854   root.role = ax::mojom::Role::kRootWebArea;
2855 
2856   ui::AXNodeData child1;
2857   child1.id = 2;
2858   child1.role = ax::mojom::Role::kStaticText;
2859   root.child_ids.push_back(2);
2860 
2861   ui::AXNodeData child2;
2862   child2.id = 3;
2863   child2.role = ax::mojom::Role::kStaticText;
2864   root.child_ids.push_back(3);
2865 
2866   ui::AXNodeData child2_child1;
2867   child2_child1.id = 4;
2868   child2_child1.role = ax::mojom::Role::kInlineTextBox;
2869   child2.child_ids.push_back(4);
2870 
2871   ui::AXNodeData child2_child2;
2872   child2_child2.id = 5;
2873   child2_child2.role = ax::mojom::Role::kInlineTextBox;
2874   child2.child_ids.push_back(5);
2875 
2876   std::unique_ptr<BrowserAccessibilityManager> manager(
2877       BrowserAccessibilityManager::Create(
2878           MakeAXTreeUpdate(root, child1, child2, child2_child1, child2_child2),
2879           test_browser_accessibility_delegate_.get()));
2880 
2881   BrowserAccessibility* root_accessible = manager->GetRoot();
2882   ASSERT_NE(nullptr, root_accessible);
2883   ASSERT_EQ(2U, root_accessible->PlatformChildCount());
2884   BrowserAccessibility* child1_accessible =
2885       root_accessible->PlatformGetChild(0);
2886   ASSERT_NE(nullptr, child1_accessible);
2887   BrowserAccessibility* child2_accessible =
2888       root_accessible->PlatformGetChild(1);
2889   ASSERT_NE(nullptr, child2_accessible);
2890   ASSERT_EQ(0U, child2_accessible->PlatformChildCount());
2891   ASSERT_EQ(2U, child2_accessible->InternalChildCount());
2892   BrowserAccessibility* child2_child1_accessible =
2893       child2_accessible->InternalGetChild(0);
2894   ASSERT_NE(nullptr, child2_child1_accessible);
2895   BrowserAccessibility* child2_child2_accessible =
2896       child2_accessible->InternalGetChild(1);
2897   ASSERT_NE(nullptr, child2_child2_accessible);
2898 
2899   EXPECT_EQ(child1_accessible, root_accessible->PlatformDeepestFirstChild());
2900   EXPECT_EQ(child1_accessible, root_accessible->InternalDeepestFirstChild());
2901 
2902   EXPECT_EQ(child2_accessible, root_accessible->PlatformDeepestLastChild());
2903   EXPECT_EQ(child2_child2_accessible,
2904             root_accessible->InternalDeepestLastChild());
2905 
2906   EXPECT_EQ(nullptr, child1_accessible->PlatformDeepestFirstChild());
2907   EXPECT_EQ(nullptr, child1_accessible->InternalDeepestFirstChild());
2908 
2909   EXPECT_EQ(nullptr, child1_accessible->PlatformDeepestLastChild());
2910   EXPECT_EQ(nullptr, child1_accessible->InternalDeepestLastChild());
2911 
2912   EXPECT_EQ(nullptr, child2_accessible->PlatformDeepestFirstChild());
2913   EXPECT_EQ(child2_child1_accessible,
2914             child2_accessible->InternalDeepestFirstChild());
2915 
2916   EXPECT_EQ(nullptr, child2_accessible->PlatformDeepestLastChild());
2917   EXPECT_EQ(child2_child2_accessible,
2918             child2_accessible->InternalDeepestLastChild());
2919 
2920   EXPECT_EQ(nullptr, child2_child1_accessible->PlatformDeepestFirstChild());
2921   EXPECT_EQ(nullptr, child2_child1_accessible->InternalDeepestFirstChild());
2922   EXPECT_EQ(nullptr, child2_child1_accessible->PlatformDeepestLastChild());
2923   EXPECT_EQ(nullptr, child2_child1_accessible->InternalDeepestLastChild());
2924   EXPECT_EQ(nullptr, child2_child2_accessible->PlatformDeepestFirstChild());
2925   EXPECT_EQ(nullptr, child2_child2_accessible->InternalDeepestFirstChild());
2926   EXPECT_EQ(nullptr, child2_child2_accessible->PlatformDeepestLastChild());
2927   EXPECT_EQ(nullptr, child2_child2_accessible->InternalDeepestLastChild());
2928 }
2929 
TEST_F(BrowserAccessibilityWinTest,TestInheritedStringAttributes)2930 TEST_F(BrowserAccessibilityWinTest, TestInheritedStringAttributes) {
2931   ui::AXNodeData root;
2932   root.id = 1;
2933   root.role = ax::mojom::Role::kRootWebArea;
2934   root.AddStringAttribute(ax::mojom::StringAttribute::kLanguage, "en-US");
2935   root.AddStringAttribute(ax::mojom::StringAttribute::kFontFamily, "Helvetica");
2936 
2937   ui::AXNodeData child1;
2938   child1.id = 2;
2939   child1.role = ax::mojom::Role::kStaticText;
2940   root.child_ids.push_back(2);
2941 
2942   ui::AXNodeData child2;
2943   child2.id = 3;
2944   child2.role = ax::mojom::Role::kStaticText;
2945   child2.AddStringAttribute(ax::mojom::StringAttribute::kLanguage, "fr");
2946   child2.AddStringAttribute(ax::mojom::StringAttribute::kFontFamily, "Arial");
2947   root.child_ids.push_back(3);
2948 
2949   ui::AXNodeData child2_child1;
2950   child2_child1.id = 4;
2951   child2_child1.role = ax::mojom::Role::kInlineTextBox;
2952   child2.child_ids.push_back(4);
2953 
2954   ui::AXNodeData child2_child2;
2955   child2_child2.id = 5;
2956   child2_child2.role = ax::mojom::Role::kInlineTextBox;
2957   child2.child_ids.push_back(5);
2958 
2959   std::unique_ptr<BrowserAccessibilityManager> manager(
2960       BrowserAccessibilityManager::Create(
2961           MakeAXTreeUpdate(root, child1, child2, child2_child1, child2_child2),
2962           test_browser_accessibility_delegate_.get()));
2963 
2964   BrowserAccessibility* root_accessible = manager->GetRoot();
2965   ASSERT_NE(nullptr, root_accessible);
2966   BrowserAccessibility* child1_accessible =
2967       root_accessible->PlatformGetChild(0);
2968   ASSERT_NE(nullptr, child1_accessible);
2969   BrowserAccessibility* child2_accessible =
2970       root_accessible->PlatformGetChild(1);
2971   ASSERT_NE(nullptr, child2_accessible);
2972   BrowserAccessibility* child2_child1_accessible =
2973       child2_accessible->InternalGetChild(0);
2974   ASSERT_NE(nullptr, child2_child1_accessible);
2975   BrowserAccessibility* child2_child2_accessible =
2976       child2_accessible->InternalGetChild(1);
2977   ASSERT_NE(nullptr, child2_child2_accessible);
2978 
2979   // Test GetInheritedString16Attribute(attribute).
2980   EXPECT_EQ(base::UTF8ToUTF16("en-US"),
2981             root_accessible->GetInheritedString16Attribute(
2982                 ax::mojom::StringAttribute::kLanguage));
2983   EXPECT_EQ(base::UTF8ToUTF16("en-US"),
2984             child1_accessible->GetInheritedString16Attribute(
2985                 ax::mojom::StringAttribute::kLanguage));
2986   EXPECT_EQ(base::UTF8ToUTF16("fr"),
2987             child2_accessible->GetInheritedString16Attribute(
2988                 ax::mojom::StringAttribute::kLanguage));
2989   EXPECT_EQ(base::UTF8ToUTF16("fr"),
2990             child2_child1_accessible->GetInheritedString16Attribute(
2991                 ax::mojom::StringAttribute::kLanguage));
2992   EXPECT_EQ(base::UTF8ToUTF16("fr"),
2993             child2_child2_accessible->GetInheritedString16Attribute(
2994                 ax::mojom::StringAttribute::kLanguage));
2995 
2996   // Test GetInheritedStringAttribute(attribute).
2997   EXPECT_EQ("Helvetica", root_accessible->GetInheritedStringAttribute(
2998                              ax::mojom::StringAttribute::kFontFamily));
2999   EXPECT_EQ("Helvetica", child1_accessible->GetInheritedStringAttribute(
3000                              ax::mojom::StringAttribute::kFontFamily));
3001   EXPECT_EQ("Arial", child2_accessible->GetInheritedStringAttribute(
3002                          ax::mojom::StringAttribute::kFontFamily));
3003   EXPECT_EQ("Arial", child2_child1_accessible->GetInheritedStringAttribute(
3004                          ax::mojom::StringAttribute::kFontFamily));
3005   EXPECT_EQ("Arial", child2_child2_accessible->GetInheritedStringAttribute(
3006                          ax::mojom::StringAttribute::kFontFamily));
3007 }
3008 
TEST_F(BrowserAccessibilityWinTest,UniqueIdWinInvalidAfterDeletingTree)3009 TEST_F(BrowserAccessibilityWinTest, UniqueIdWinInvalidAfterDeletingTree) {
3010   ui::AXNodeData root_node;
3011   root_node.id = 1;
3012   root_node.role = ax::mojom::Role::kRootWebArea;
3013 
3014   ui::AXNodeData child_node;
3015   child_node.id = 2;
3016   root_node.child_ids.push_back(2);
3017 
3018   std::unique_ptr<BrowserAccessibilityManagerWin> manager(
3019       new BrowserAccessibilityManagerWin(
3020           MakeAXTreeUpdate(root_node, child_node),
3021           test_browser_accessibility_delegate_.get()));
3022 
3023   BrowserAccessibility* root = manager->GetRoot();
3024   int32_t root_unique_id = GetUniqueId(root);
3025   BrowserAccessibility* child = root->PlatformGetChild(0);
3026   int32_t child_unique_id = GetUniqueId(child);
3027 
3028   // Now destroy that original tree and create a new tree.
3029   manager.reset(new BrowserAccessibilityManagerWin(
3030       MakeAXTreeUpdate(root_node, child_node),
3031       test_browser_accessibility_delegate_.get()));
3032   root = manager->GetRoot();
3033   int32_t root_unique_id_2 = GetUniqueId(root);
3034   child = root->PlatformGetChild(0);
3035   int32_t child_unique_id_2 = GetUniqueId(child);
3036 
3037   // The nodes in the new tree should not have the same ids.
3038   EXPECT_NE(root_unique_id, root_unique_id_2);
3039   EXPECT_NE(child_unique_id, child_unique_id_2);
3040 
3041   // Trying to access the unique IDs of the old, deleted objects should fail.
3042   base::win::ScopedVariant old_root_variant(-root_unique_id);
3043   Microsoft::WRL::ComPtr<IDispatch> old_root_dispatch;
3044   HRESULT hr = ToBrowserAccessibilityWin(root)->GetCOM()->get_accChild(
3045       old_root_variant, &old_root_dispatch);
3046   EXPECT_EQ(E_INVALIDARG, hr);
3047 
3048   base::win::ScopedVariant old_child_variant(-child_unique_id);
3049   Microsoft::WRL::ComPtr<IDispatch> old_child_dispatch;
3050   hr = ToBrowserAccessibilityWin(root)->GetCOM()->get_accChild(
3051       old_child_variant, &old_child_dispatch);
3052   EXPECT_EQ(E_INVALIDARG, hr);
3053 
3054   // Trying to access the unique IDs of the new objects should succeed.
3055   base::win::ScopedVariant new_root_variant(-root_unique_id_2);
3056   Microsoft::WRL::ComPtr<IDispatch> new_root_dispatch;
3057   hr = ToBrowserAccessibilityWin(root)->GetCOM()->get_accChild(
3058       new_root_variant, &new_root_dispatch);
3059   EXPECT_EQ(S_OK, hr);
3060 
3061   base::win::ScopedVariant new_child_variant(-child_unique_id_2);
3062   Microsoft::WRL::ComPtr<IDispatch> new_child_dispatch;
3063   hr = ToBrowserAccessibilityWin(root)->GetCOM()->get_accChild(
3064       new_child_variant, &new_child_dispatch);
3065   EXPECT_EQ(S_OK, hr);
3066 }
3067 
TEST_F(BrowserAccessibilityWinTest,AccChildOnlyReturnsDescendants)3068 TEST_F(BrowserAccessibilityWinTest, AccChildOnlyReturnsDescendants) {
3069   ui::AXNodeData root_node;
3070   root_node.id = 1;
3071   root_node.role = ax::mojom::Role::kRootWebArea;
3072 
3073   ui::AXNodeData child_node;
3074   child_node.id = 2;
3075   root_node.child_ids.push_back(2);
3076 
3077   std::unique_ptr<BrowserAccessibilityManagerWin> manager(
3078       new BrowserAccessibilityManagerWin(
3079           MakeAXTreeUpdate(root_node, child_node),
3080           test_browser_accessibility_delegate_.get()));
3081 
3082   BrowserAccessibility* root = manager->GetRoot();
3083   BrowserAccessibility* child = root->PlatformGetChild(0);
3084 
3085   base::win::ScopedVariant root_unique_id_variant(-GetUniqueId(root));
3086   Microsoft::WRL::ComPtr<IDispatch> result;
3087   EXPECT_EQ(E_INVALIDARG,
3088             ToBrowserAccessibilityWin(child)->GetCOM()->get_accChild(
3089                 root_unique_id_variant, &result));
3090 
3091   base::win::ScopedVariant child_unique_id_variant(-GetUniqueId(child));
3092   EXPECT_EQ(S_OK, ToBrowserAccessibilityWin(root)->GetCOM()->get_accChild(
3093                       child_unique_id_variant, &result));
3094 }
3095 
3096 // TODO(crbug.com/929563): Disabled due to flakiness.
TEST_F(BrowserAccessibilityWinTest,DISABLED_TestIAccessible2Relations)3097 TEST_F(BrowserAccessibilityWinTest, DISABLED_TestIAccessible2Relations) {
3098   ui::AXNodeData root;
3099   root.id = 1;
3100   root.role = ax::mojom::Role::kRootWebArea;
3101   // Reflexive relations should be ignored.
3102   std::vector<int32_t> describedby_ids = {1, 2, 3};
3103   root.AddIntListAttribute(ax::mojom::IntListAttribute::kDescribedbyIds,
3104                            describedby_ids);
3105 
3106   ui::AXNodeData child1;
3107   child1.id = 2;
3108   child1.role = ax::mojom::Role::kStaticText;
3109   root.child_ids.push_back(2);
3110 
3111   ui::AXNodeData child2;
3112   child2.id = 3;
3113   child2.role = ax::mojom::Role::kStaticText;
3114   root.child_ids.push_back(3);
3115 
3116   std::unique_ptr<BrowserAccessibilityManager> manager(
3117       BrowserAccessibilityManager::Create(
3118           MakeAXTreeUpdate(root, child1, child2),
3119           test_browser_accessibility_delegate_.get()));
3120 
3121   BrowserAccessibilityWin* ax_root =
3122       ToBrowserAccessibilityWin(manager->GetRoot());
3123   ASSERT_NE(nullptr, ax_root);
3124   BrowserAccessibilityWin* ax_child1 =
3125       ToBrowserAccessibilityWin(ax_root->PlatformGetChild(0));
3126   ASSERT_NE(nullptr, ax_child1);
3127   BrowserAccessibilityWin* ax_child2 =
3128       ToBrowserAccessibilityWin(ax_root->PlatformGetChild(1));
3129   ASSERT_NE(nullptr, ax_child2);
3130 
3131   LONG n_relations = 0;
3132   LONG n_targets = 0;
3133   LONG unique_id = 0;
3134   base::win::ScopedBstr relation_type;
3135   Microsoft::WRL::ComPtr<IAccessibleRelation> describedby_relation;
3136   Microsoft::WRL::ComPtr<IAccessibleRelation> description_for_relation;
3137   Microsoft::WRL::ComPtr<IUnknown> target;
3138   Microsoft::WRL::ComPtr<IAccessible2> ax_target;
3139 
3140   EXPECT_HRESULT_SUCCEEDED(ax_root->GetCOM()->get_nRelations(&n_relations));
3141   EXPECT_EQ(1, n_relations);
3142 
3143   EXPECT_HRESULT_SUCCEEDED(
3144       ax_root->GetCOM()->get_relation(0, &describedby_relation));
3145   EXPECT_HRESULT_SUCCEEDED(
3146       describedby_relation->get_relationType(relation_type.Receive()));
3147   EXPECT_EQ(L"describedBy", base::string16(relation_type.Get()));
3148   relation_type.Reset();
3149 
3150   EXPECT_HRESULT_SUCCEEDED(describedby_relation->get_nTargets(&n_targets));
3151   EXPECT_EQ(2, n_targets);
3152 
3153   EXPECT_HRESULT_SUCCEEDED(describedby_relation->get_target(0, &target));
3154   target.As(&ax_target);
3155   EXPECT_HRESULT_SUCCEEDED(ax_target->get_uniqueID(&unique_id));
3156   EXPECT_EQ(-GetUniqueId(ax_child1), unique_id);
3157   ax_target.Reset();
3158   target.Reset();
3159 
3160   EXPECT_HRESULT_SUCCEEDED(describedby_relation->get_target(1, &target));
3161   target.As(&ax_target);
3162   EXPECT_HRESULT_SUCCEEDED(ax_target->get_uniqueID(&unique_id));
3163   EXPECT_EQ(-GetUniqueId(ax_child2), unique_id);
3164   ax_target.Reset();
3165   target.Reset();
3166   describedby_relation.Reset();
3167 
3168   // Test the reverse relations.
3169   EXPECT_HRESULT_SUCCEEDED(ax_child1->GetCOM()->get_nRelations(&n_relations));
3170   EXPECT_EQ(1, n_relations);
3171 
3172   EXPECT_HRESULT_SUCCEEDED(
3173       ax_child1->GetCOM()->get_relation(0, &description_for_relation));
3174   EXPECT_HRESULT_SUCCEEDED(
3175       description_for_relation->get_relationType(relation_type.Receive()));
3176   EXPECT_EQ(L"descriptionFor", base::string16(relation_type.Get()));
3177   relation_type.Reset();
3178 
3179   EXPECT_HRESULT_SUCCEEDED(description_for_relation->get_nTargets(&n_targets));
3180   EXPECT_EQ(1, n_targets);
3181 
3182   EXPECT_HRESULT_SUCCEEDED(description_for_relation->get_target(0, &target));
3183   target.As(&ax_target);
3184   EXPECT_HRESULT_SUCCEEDED(ax_target->get_uniqueID(&unique_id));
3185   EXPECT_EQ(-GetUniqueId(ax_root), unique_id);
3186   ax_target.Reset();
3187   target.Reset();
3188   description_for_relation.Reset();
3189 
3190   EXPECT_HRESULT_SUCCEEDED(ax_child2->GetCOM()->get_nRelations(&n_relations));
3191   EXPECT_EQ(1, n_relations);
3192 
3193   EXPECT_HRESULT_SUCCEEDED(
3194       ax_child2->GetCOM()->get_relation(0, &description_for_relation));
3195   EXPECT_HRESULT_SUCCEEDED(
3196       description_for_relation->get_relationType(relation_type.Receive()));
3197   EXPECT_EQ(L"descriptionFor", base::string16(relation_type.Get()));
3198   relation_type.Reset();
3199 
3200   EXPECT_HRESULT_SUCCEEDED(description_for_relation->get_nTargets(&n_targets));
3201   EXPECT_EQ(1, n_targets);
3202 
3203   EXPECT_HRESULT_SUCCEEDED(description_for_relation->get_target(0, &target));
3204   target.As(&ax_target);
3205   EXPECT_HRESULT_SUCCEEDED(ax_target->get_uniqueID(&unique_id));
3206   EXPECT_EQ(-GetUniqueId(ax_root), unique_id);
3207   ax_target.Reset();
3208   target.Reset();
3209 
3210   // Try adding one more relation.
3211   std::vector<int32_t> labelledby_ids = {3};
3212   child1.AddIntListAttribute(ax::mojom::IntListAttribute::kLabelledbyIds,
3213                              labelledby_ids);
3214   AXEventNotificationDetails event_bundle;
3215   event_bundle.updates.resize(1);
3216   event_bundle.updates[0].nodes.push_back(child1);
3217   ASSERT_TRUE(manager->OnAccessibilityEvents(event_bundle));
3218 
3219   EXPECT_HRESULT_SUCCEEDED(ax_child1->GetCOM()->get_nRelations(&n_relations));
3220   EXPECT_EQ(2, n_relations);
3221   EXPECT_HRESULT_SUCCEEDED(ax_child2->GetCOM()->get_nRelations(&n_relations));
3222   EXPECT_EQ(2, n_relations);
3223 }
3224 
TEST_F(BrowserAccessibilityWinTest,TestUIASwitch)3225 TEST_F(BrowserAccessibilityWinTest, TestUIASwitch) {
3226   EXPECT_FALSE(::switches::IsExperimentalAccessibilityPlatformUIAEnabled());
3227 
3228   base::CommandLine::ForCurrentProcess()->AppendSwitch(
3229       ::switches::kEnableExperimentalUIAutomation);
3230 
3231   EXPECT_TRUE(::switches::IsExperimentalAccessibilityPlatformUIAEnabled());
3232 }
3233 
3234 }  // namespace content
3235