• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <vector>
6 
7 #include "base/memory/scoped_ptr.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "base/win/scoped_bstr.h"
10 #include "base/win/scoped_comptr.h"
11 #include "base/win/scoped_variant.h"
12 #include "content/browser/accessibility/accessibility_mode_helper.h"
13 #include "content/browser/accessibility/accessibility_tree_formatter_utils_win.h"
14 #include "content/browser/renderer_host/render_view_host_impl.h"
15 #include "content/public/browser/notification_service.h"
16 #include "content/public/browser/notification_types.h"
17 #include "content/public/browser/render_frame_host.h"
18 #include "content/public/browser/render_widget_host_view.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/common/url_constants.h"
21 #include "content/public/test/content_browser_test.h"
22 #include "content/public/test/content_browser_test_utils.h"
23 #include "content/shell/browser/shell.h"
24 #include "content/test/accessibility_browser_test_utils.h"
25 #include "third_party/iaccessible2/ia2_api_all.h"
26 #include "third_party/isimpledom/ISimpleDOMNode.h"
27 #include "ui/aura/window.h"
28 #include "ui/aura/window_tree_host.h"
29 
30 namespace content {
31 
32 namespace {
33 
34 // Helpers --------------------------------------------------------------------
35 
GetAccessibleFromResultVariant(IAccessible * parent,VARIANT * var)36 base::win::ScopedComPtr<IAccessible> GetAccessibleFromResultVariant(
37     IAccessible* parent,
38     VARIANT* var) {
39   base::win::ScopedComPtr<IAccessible> ptr;
40   switch (V_VT(var)) {
41     case VT_DISPATCH: {
42       IDispatch* dispatch = V_DISPATCH(var);
43       if (dispatch)
44         ptr.QueryFrom(dispatch);
45       break;
46     }
47 
48     case VT_I4: {
49       base::win::ScopedComPtr<IDispatch> dispatch;
50       HRESULT hr = parent->get_accChild(*var, dispatch.Receive());
51       EXPECT_TRUE(SUCCEEDED(hr));
52       if (dispatch)
53         dispatch.QueryInterface(ptr.Receive());
54       break;
55     }
56   }
57   return ptr;
58 }
59 
QueryIAccessible2(IAccessible * accessible,IAccessible2 ** accessible2)60 HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2) {
61   // TODO(ctguil): For some reason querying the IAccessible2 interface from
62   // IAccessible fails.
63   base::win::ScopedComPtr<IServiceProvider> service_provider;
64   HRESULT hr = accessible->QueryInterface(service_provider.Receive());
65   return SUCCEEDED(hr) ?
66       service_provider->QueryService(IID_IAccessible2, accessible2) : hr;
67 }
68 
69 // Recursively search through all of the descendants reachable from an
70 // IAccessible node and return true if we find one with the given role
71 // and name.
RecursiveFindNodeInAccessibilityTree(IAccessible * node,int32 expected_role,const std::wstring & expected_name,int32 depth,bool * found)72 void RecursiveFindNodeInAccessibilityTree(IAccessible* node,
73                                           int32 expected_role,
74                                           const std::wstring& expected_name,
75                                           int32 depth,
76                                           bool* found) {
77   base::win::ScopedBstr name_bstr;
78   base::win::ScopedVariant childid_self(CHILDID_SELF);
79   node->get_accName(childid_self, name_bstr.Receive());
80   std::wstring name(name_bstr, name_bstr.Length());
81   base::win::ScopedVariant role;
82   node->get_accRole(childid_self, role.Receive());
83   ASSERT_EQ(VT_I4, role.type());
84 
85   // Print the accessibility tree as we go, because if this test fails
86   // on the bots, this is really helpful in figuring out why.
87   for (int i = 0; i < depth; i++)
88     printf("  ");
89   printf("role=%s name=%s\n",
90       base::WideToUTF8(IAccessibleRoleToString(V_I4(&role))).c_str(),
91       base::WideToUTF8(name).c_str());
92 
93   if (expected_role == V_I4(&role) && expected_name == name) {
94     *found = true;
95     return;
96   }
97 
98   LONG child_count = 0;
99   HRESULT hr = node->get_accChildCount(&child_count);
100   ASSERT_EQ(S_OK, hr);
101 
102   scoped_ptr<VARIANT[]> child_array(new VARIANT[child_count]);
103   LONG obtained_count = 0;
104   hr = AccessibleChildren(
105       node, 0, child_count, child_array.get(), &obtained_count);
106   ASSERT_EQ(S_OK, hr);
107   ASSERT_EQ(child_count, obtained_count);
108 
109   for (int index = 0; index < obtained_count; index++) {
110     base::win::ScopedComPtr<IAccessible> child_accessible(
111         GetAccessibleFromResultVariant(node, &child_array.get()[index]));
112     if (child_accessible) {
113       RecursiveFindNodeInAccessibilityTree(
114           child_accessible.get(), expected_role, expected_name, depth + 1,
115           found);
116       if (*found)
117         return;
118     }
119   }
120 }
121 
122 
123 // AccessibilityWinBrowserTest ------------------------------------------------
124 
125 class AccessibilityWinBrowserTest : public ContentBrowserTest {
126  public:
127   AccessibilityWinBrowserTest();
128   virtual ~AccessibilityWinBrowserTest();
129 
130  protected:
131   void LoadInitialAccessibilityTreeFromHtml(const std::string& html);
132   IAccessible* GetRendererAccessible();
133   void ExecuteScript(const std::wstring& script);
134 
135  private:
136   DISALLOW_COPY_AND_ASSIGN(AccessibilityWinBrowserTest);
137 };
138 
AccessibilityWinBrowserTest()139 AccessibilityWinBrowserTest::AccessibilityWinBrowserTest() {
140 }
141 
~AccessibilityWinBrowserTest()142 AccessibilityWinBrowserTest::~AccessibilityWinBrowserTest() {
143 }
144 
LoadInitialAccessibilityTreeFromHtml(const std::string & html)145 void AccessibilityWinBrowserTest::LoadInitialAccessibilityTreeFromHtml(
146     const std::string& html) {
147   AccessibilityNotificationWaiter waiter(
148       shell(), AccessibilityModeComplete,
149       ui::AX_EVENT_LOAD_COMPLETE);
150   GURL html_data_url("data:text/html," + html);
151   NavigateToURL(shell(), html_data_url);
152   waiter.WaitForNotification();
153 }
154 
155 // Retrieve the MSAA client accessibility object for the Render Widget Host View
156 // of the selected tab.
GetRendererAccessible()157 IAccessible* AccessibilityWinBrowserTest::GetRendererAccessible() {
158   content::WebContents* web_contents = shell()->web_contents();
159   return web_contents->GetRenderWidgetHostView()->GetNativeViewAccessible();
160 }
161 
ExecuteScript(const std::wstring & script)162 void AccessibilityWinBrowserTest::ExecuteScript(const std::wstring& script) {
163   shell()->web_contents()->GetMainFrame()->ExecuteJavaScript(script);
164 }
165 
166 
167 // AccessibleChecker ----------------------------------------------------------
168 
169 class AccessibleChecker {
170  public:
171   // This constructor can be used if the IA2 role will be the same as the MSAA
172   // role.
173   AccessibleChecker(const std::wstring& expected_name,
174                     int32 expected_role,
175                     const std::wstring& expected_value);
176   AccessibleChecker(const std::wstring& expected_name,
177                     int32 expected_role,
178                     int32 expected_ia2_role,
179                     const std::wstring& expected_value);
180   AccessibleChecker(const std::wstring& expected_name,
181                     const std::wstring& expected_role,
182                     int32 expected_ia2_role,
183                     const std::wstring& expected_value);
184 
185   // Append an AccessibleChecker that verifies accessibility information for
186   // a child IAccessible. Order is important.
187   void AppendExpectedChild(AccessibleChecker* expected_child);
188 
189   // Check that the name and role of the given IAccessible instance and its
190   // descendants match the expected names and roles that this object was
191   // initialized with.
192   void CheckAccessible(IAccessible* accessible);
193 
194   // Set the expected value for this AccessibleChecker.
195   void SetExpectedValue(const std::wstring& expected_value);
196 
197   // Set the expected state for this AccessibleChecker.
198   void SetExpectedState(LONG expected_state);
199 
200  private:
201   typedef std::vector<AccessibleChecker*> AccessibleCheckerVector;
202 
203   void CheckAccessibleName(IAccessible* accessible);
204   void CheckAccessibleRole(IAccessible* accessible);
205   void CheckIA2Role(IAccessible* accessible);
206   void CheckAccessibleValue(IAccessible* accessible);
207   void CheckAccessibleState(IAccessible* accessible);
208   void CheckAccessibleChildren(IAccessible* accessible);
209   base::string16 RoleVariantToString(const base::win::ScopedVariant& role);
210 
211   // Expected accessible name. Checked against IAccessible::get_accName.
212   std::wstring name_;
213 
214   // Expected accessible role. Checked against IAccessible::get_accRole.
215   base::win::ScopedVariant role_;
216 
217   // Expected IAccessible2 role. Checked against IAccessible2::role.
218   int32 ia2_role_;
219 
220   // Expected accessible value. Checked against IAccessible::get_accValue.
221   std::wstring value_;
222 
223   // Expected accessible state. Checked against IAccessible::get_accState.
224   LONG state_;
225 
226   // Expected accessible children. Checked using IAccessible::get_accChildCount
227   // and ::AccessibleChildren.
228   AccessibleCheckerVector children_;
229 };
230 
AccessibleChecker(const std::wstring & expected_name,int32 expected_role,const std::wstring & expected_value)231 AccessibleChecker::AccessibleChecker(const std::wstring& expected_name,
232                                      int32 expected_role,
233                                      const std::wstring& expected_value)
234     : name_(expected_name),
235       role_(expected_role),
236       ia2_role_(expected_role),
237       value_(expected_value),
238       state_(-1) {
239 }
240 
AccessibleChecker(const std::wstring & expected_name,int32 expected_role,int32 expected_ia2_role,const std::wstring & expected_value)241 AccessibleChecker::AccessibleChecker(const std::wstring& expected_name,
242                                      int32 expected_role,
243                                      int32 expected_ia2_role,
244                                      const std::wstring& expected_value)
245     : name_(expected_name),
246       role_(expected_role),
247       ia2_role_(expected_ia2_role),
248       value_(expected_value),
249       state_(-1) {
250 }
251 
AccessibleChecker(const std::wstring & expected_name,const std::wstring & expected_role,int32 expected_ia2_role,const std::wstring & expected_value)252 AccessibleChecker::AccessibleChecker(const std::wstring& expected_name,
253                                      const std::wstring& expected_role,
254                                      int32 expected_ia2_role,
255                                      const std::wstring& expected_value)
256     : name_(expected_name),
257       role_(expected_role.c_str()),
258       ia2_role_(expected_ia2_role),
259       value_(expected_value),
260       state_(-1) {
261 }
262 
AppendExpectedChild(AccessibleChecker * expected_child)263 void AccessibleChecker::AppendExpectedChild(
264     AccessibleChecker* expected_child) {
265   children_.push_back(expected_child);
266 }
267 
CheckAccessible(IAccessible * accessible)268 void AccessibleChecker::CheckAccessible(IAccessible* accessible) {
269   SCOPED_TRACE("while checking " +
270                    base::UTF16ToUTF8(RoleVariantToString(role_)));
271   CheckAccessibleName(accessible);
272   CheckAccessibleRole(accessible);
273   CheckIA2Role(accessible);
274   CheckAccessibleValue(accessible);
275   CheckAccessibleState(accessible);
276   CheckAccessibleChildren(accessible);
277 }
278 
SetExpectedValue(const std::wstring & expected_value)279 void AccessibleChecker::SetExpectedValue(const std::wstring& expected_value) {
280   value_ = expected_value;
281 }
282 
SetExpectedState(LONG expected_state)283 void AccessibleChecker::SetExpectedState(LONG expected_state) {
284   state_ = expected_state;
285 }
286 
CheckAccessibleName(IAccessible * accessible)287 void AccessibleChecker::CheckAccessibleName(IAccessible* accessible) {
288   base::win::ScopedBstr name;
289   base::win::ScopedVariant childid_self(CHILDID_SELF);
290   HRESULT hr = accessible->get_accName(childid_self, name.Receive());
291 
292   if (name_.empty()) {
293     // If the object doesn't have name S_FALSE should be returned.
294     EXPECT_EQ(S_FALSE, hr);
295   } else {
296     // Test that the correct string was returned.
297     EXPECT_EQ(S_OK, hr);
298     EXPECT_EQ(name_, std::wstring(name, name.Length()));
299   }
300 }
301 
CheckAccessibleRole(IAccessible * accessible)302 void AccessibleChecker::CheckAccessibleRole(IAccessible* accessible) {
303   base::win::ScopedVariant role;
304   base::win::ScopedVariant childid_self(CHILDID_SELF);
305   HRESULT hr = accessible->get_accRole(childid_self, role.Receive());
306   ASSERT_EQ(S_OK, hr);
307   EXPECT_EQ(0, role_.Compare(role))
308       << "Expected role: " << RoleVariantToString(role_)
309       << "\nGot role: " << RoleVariantToString(role);
310 }
311 
CheckIA2Role(IAccessible * accessible)312 void AccessibleChecker::CheckIA2Role(IAccessible* accessible) {
313   base::win::ScopedComPtr<IAccessible2> accessible2;
314   HRESULT hr = QueryIAccessible2(accessible, accessible2.Receive());
315   ASSERT_EQ(S_OK, hr);
316   long ia2_role = 0;
317   hr = accessible2->role(&ia2_role);
318   ASSERT_EQ(S_OK, hr);
319   EXPECT_EQ(ia2_role_, ia2_role)
320     << "Expected ia2 role: " << IAccessible2RoleToString(ia2_role_)
321     << "\nGot ia2 role: " << IAccessible2RoleToString(ia2_role);
322 }
323 
CheckAccessibleValue(IAccessible * accessible)324 void AccessibleChecker::CheckAccessibleValue(IAccessible* accessible) {
325   // Don't check the value if if's a DOCUMENT role, because the value
326   // is supposed to be the url (and we don't keep track of that in the
327   // test expectations).
328   base::win::ScopedVariant role;
329   base::win::ScopedVariant childid_self(CHILDID_SELF);
330   HRESULT hr = accessible->get_accRole(childid_self, role.Receive());
331   ASSERT_EQ(S_OK, hr);
332   if (role.type() == VT_I4 && V_I4(&role) == ROLE_SYSTEM_DOCUMENT)
333     return;
334 
335   // Get the value.
336   base::win::ScopedBstr value;
337   hr = accessible->get_accValue(childid_self, value.Receive());
338   EXPECT_EQ(S_OK, hr);
339 
340   // Test that the correct string was returned.
341   EXPECT_EQ(value_, std::wstring(value, value.Length()));
342 }
343 
CheckAccessibleState(IAccessible * accessible)344 void AccessibleChecker::CheckAccessibleState(IAccessible* accessible) {
345   if (state_ < 0)
346     return;
347 
348   base::win::ScopedVariant state;
349   base::win::ScopedVariant childid_self(CHILDID_SELF);
350   HRESULT hr = accessible->get_accState(childid_self, state.Receive());
351   EXPECT_EQ(S_OK, hr);
352   ASSERT_EQ(VT_I4, state.type());
353   LONG obj_state = V_I4(&state);
354   // Avoid flakiness. The "offscreen" state depends on whether the browser
355   // window is frontmost or not, and "hottracked" depends on whether the
356   // mouse cursor happens to be over the element.
357   obj_state &= ~(STATE_SYSTEM_OFFSCREEN | STATE_SYSTEM_HOTTRACKED);
358   EXPECT_EQ(state_, obj_state)
359     << "Expected state: " << IAccessibleStateToString(state_)
360     << "\nGot state: " << IAccessibleStateToString(obj_state);
361 }
362 
CheckAccessibleChildren(IAccessible * parent)363 void AccessibleChecker::CheckAccessibleChildren(IAccessible* parent) {
364   LONG child_count = 0;
365   HRESULT hr = parent->get_accChildCount(&child_count);
366   EXPECT_EQ(S_OK, hr);
367   ASSERT_EQ(child_count, children_.size());
368 
369   scoped_ptr<VARIANT[]> child_array(new VARIANT[child_count]);
370   LONG obtained_count = 0;
371   hr = AccessibleChildren(parent, 0, child_count,
372                           child_array.get(), &obtained_count);
373   ASSERT_EQ(S_OK, hr);
374   ASSERT_EQ(child_count, obtained_count);
375 
376   VARIANT* child = child_array.get();
377   for (AccessibleCheckerVector::iterator child_checker = children_.begin();
378        child_checker != children_.end();
379        ++child_checker, ++child) {
380     base::win::ScopedComPtr<IAccessible> child_accessible(
381         GetAccessibleFromResultVariant(parent, child));
382     ASSERT_TRUE(child_accessible.get());
383     (*child_checker)->CheckAccessible(child_accessible);
384   }
385 }
386 
RoleVariantToString(const base::win::ScopedVariant & role)387 base::string16 AccessibleChecker::RoleVariantToString(
388     const base::win::ScopedVariant& role) {
389   if (role.type() == VT_I4)
390     return IAccessibleRoleToString(V_I4(&role));
391   if (role.type() == VT_BSTR)
392     return base::string16(V_BSTR(&role), SysStringLen(V_BSTR(&role)));
393   return base::string16();
394 }
395 
396 }  // namespace
397 
398 
399 // Tests ----------------------------------------------------------------------
400 
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestBusyAccessibilityTree)401 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
402                        TestBusyAccessibilityTree) {
403  if (GetBaseAccessibilityMode() != AccessibilityModeOff)
404     return;
405 
406   NavigateToURL(shell(), GURL(url::kAboutBlankURL));
407 
408   // The initial accessible returned should have state STATE_SYSTEM_BUSY while
409   // the accessibility tree is being requested from the renderer.
410   AccessibleChecker document1_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
411                                       std::wstring());
412   document1_checker.SetExpectedState(
413       STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED |
414       STATE_SYSTEM_BUSY);
415   document1_checker.CheckAccessible(GetRendererAccessible());
416 }
417 
418 // Periodically failing.  See crbug.com/145537
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,DISABLED_TestNotificationActiveDescendantChanged)419 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
420                        DISABLED_TestNotificationActiveDescendantChanged) {
421   LoadInitialAccessibilityTreeFromHtml(
422       "<ul tabindex='-1' role='radiogroup' aria-label='ul'>"
423       "<li id='li'>li</li></ul>");
424 
425   // Check the browser's copy of the renderer accessibility tree.
426   AccessibleChecker list_marker_checker(L"\x2022", ROLE_SYSTEM_TEXT,
427                                         std::wstring());
428   AccessibleChecker static_text_checker(L"li", ROLE_SYSTEM_TEXT,
429                                         std::wstring());
430   AccessibleChecker list_item_checker(std::wstring(), ROLE_SYSTEM_LISTITEM,
431                                       std::wstring());
432   list_item_checker.SetExpectedState(STATE_SYSTEM_READONLY);
433   AccessibleChecker radio_group_checker(L"ul", ROLE_SYSTEM_GROUPING,
434                                         IA2_ROLE_SECTION, std::wstring());
435   radio_group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
436   AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
437                                      std::wstring());
438   list_item_checker.AppendExpectedChild(&list_marker_checker);
439   list_item_checker.AppendExpectedChild(&static_text_checker);
440   radio_group_checker.AppendExpectedChild(&list_item_checker);
441   document_checker.AppendExpectedChild(&radio_group_checker);
442   document_checker.CheckAccessible(GetRendererAccessible());
443 
444   // Set focus to the radio group.
445   scoped_ptr<AccessibilityNotificationWaiter> waiter(
446       new AccessibilityNotificationWaiter(
447           shell(), AccessibilityModeComplete,
448           ui::AX_EVENT_FOCUS));
449   ExecuteScript(L"document.body.children[0].focus()");
450   waiter->WaitForNotification();
451 
452   // Check that the accessibility tree of the browser has been updated.
453   radio_group_checker.SetExpectedState(
454       STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED);
455   document_checker.CheckAccessible(GetRendererAccessible());
456 
457   // Set the active descendant of the radio group
458   waiter.reset(new AccessibilityNotificationWaiter(
459       shell(), AccessibilityModeComplete,
460       ui::AX_EVENT_FOCUS));
461   ExecuteScript(
462       L"document.body.children[0].setAttribute('aria-activedescendant', 'li')");
463   waiter->WaitForNotification();
464 
465   // Check that the accessibility tree of the browser has been updated.
466   list_item_checker.SetExpectedState(
467       STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSED);
468   radio_group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
469   document_checker.CheckAccessible(GetRendererAccessible());
470 }
471 
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationCheckedStateChanged)472 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
473                        TestNotificationCheckedStateChanged) {
474   LoadInitialAccessibilityTreeFromHtml(
475       "<body><input type='checkbox' /></body>");
476 
477   // Check the browser's copy of the renderer accessibility tree.
478   AccessibleChecker checkbox_checker(std::wstring(), ROLE_SYSTEM_CHECKBUTTON,
479                                      std::wstring());
480   checkbox_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
481   AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION,
482                                  std::wstring());
483   AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
484                                      std::wstring());
485   body_checker.AppendExpectedChild(&checkbox_checker);
486   document_checker.AppendExpectedChild(&body_checker);
487   document_checker.CheckAccessible(GetRendererAccessible());
488 
489   // Check the checkbox.
490   scoped_ptr<AccessibilityNotificationWaiter> waiter(
491       new AccessibilityNotificationWaiter(
492           shell(), AccessibilityModeComplete,
493           ui::AX_EVENT_CHECKED_STATE_CHANGED));
494   ExecuteScript(L"document.body.children[0].checked=true");
495   waiter->WaitForNotification();
496 
497   // Check that the accessibility tree of the browser has been updated.
498   checkbox_checker.SetExpectedState(
499       STATE_SYSTEM_CHECKED | STATE_SYSTEM_FOCUSABLE);
500   document_checker.CheckAccessible(GetRendererAccessible());
501 }
502 
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationChildrenChanged)503 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
504                        TestNotificationChildrenChanged) {
505   // The role attribute causes the node to be in the accessibility tree.
506   LoadInitialAccessibilityTreeFromHtml("<body role=group></body>");
507 
508   // Check the browser's copy of the renderer accessibility tree.
509   AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
510                                   std::wstring());
511   AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
512                                      std::wstring());
513   document_checker.AppendExpectedChild(&group_checker);
514   document_checker.CheckAccessible(GetRendererAccessible());
515 
516   // Change the children of the document body.
517   scoped_ptr<AccessibilityNotificationWaiter> waiter(
518       new AccessibilityNotificationWaiter(
519           shell(),
520           AccessibilityModeComplete,
521           ui::AX_EVENT_CHILDREN_CHANGED));
522   ExecuteScript(L"document.body.innerHTML='<b>new text</b>'");
523   waiter->WaitForNotification();
524 
525   // Check that the accessibility tree of the browser has been updated.
526   AccessibleChecker text_checker(
527       L"new text", ROLE_SYSTEM_STATICTEXT, std::wstring());
528   group_checker.AppendExpectedChild(&text_checker);
529   document_checker.CheckAccessible(GetRendererAccessible());
530 }
531 
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationChildrenChanged2)532 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
533                        TestNotificationChildrenChanged2) {
534   // The role attribute causes the node to be in the accessibility tree.
535   LoadInitialAccessibilityTreeFromHtml(
536       "<div role=group style='visibility: hidden'>text</div>");
537 
538   // Check the accessible tree of the browser.
539   AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
540                                      std::wstring());
541   document_checker.CheckAccessible(GetRendererAccessible());
542 
543   // Change the children of the document body.
544   scoped_ptr<AccessibilityNotificationWaiter> waiter(
545       new AccessibilityNotificationWaiter(
546           shell(), AccessibilityModeComplete,
547           ui::AX_EVENT_CHILDREN_CHANGED));
548   ExecuteScript(L"document.body.children[0].style.visibility='visible'");
549   waiter->WaitForNotification();
550 
551   // Check that the accessibility tree of the browser has been updated.
552   AccessibleChecker static_text_checker(L"text", ROLE_SYSTEM_STATICTEXT,
553                                         std::wstring());
554   AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
555                                   std::wstring());
556   document_checker.AppendExpectedChild(&group_checker);
557   group_checker.AppendExpectedChild(&static_text_checker);
558   document_checker.CheckAccessible(GetRendererAccessible());
559 }
560 
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationFocusChanged)561 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
562                        TestNotificationFocusChanged) {
563   // The role attribute causes the node to be in the accessibility tree.
564   LoadInitialAccessibilityTreeFromHtml("<div role=group tabindex='-1'></div>");
565 
566   // Check the browser's copy of the renderer accessibility tree.
567   SCOPED_TRACE("Check initial tree");
568   AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
569                                   std::wstring());
570   group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
571   AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
572                                      std::wstring());
573   document_checker.AppendExpectedChild(&group_checker);
574   document_checker.CheckAccessible(GetRendererAccessible());
575 
576   // Focus the div in the document
577   scoped_ptr<AccessibilityNotificationWaiter> waiter(
578       new AccessibilityNotificationWaiter(
579           shell(), AccessibilityModeComplete,
580           ui::AX_EVENT_FOCUS));
581   ExecuteScript(L"document.body.children[0].focus()");
582   waiter->WaitForNotification();
583 
584   // Check that the accessibility tree of the browser has been updated.
585   SCOPED_TRACE("Check updated tree after focusing div");
586   group_checker.SetExpectedState(
587       STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED);
588   document_checker.CheckAccessible(GetRendererAccessible());
589 
590   // Focus the document accessible. This will un-focus the current node.
591   waiter.reset(
592       new AccessibilityNotificationWaiter(
593           shell(), AccessibilityModeComplete,
594           ui::AX_EVENT_BLUR));
595   base::win::ScopedComPtr<IAccessible> document_accessible(
596       GetRendererAccessible());
597   ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
598   base::win::ScopedVariant childid_self(CHILDID_SELF);
599   HRESULT hr = document_accessible->accSelect(SELFLAG_TAKEFOCUS, childid_self);
600   ASSERT_EQ(S_OK, hr);
601   waiter->WaitForNotification();
602 
603   // Check that the accessibility tree of the browser has been updated.
604   SCOPED_TRACE("Check updated tree after focusing document again");
605   group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
606   document_checker.CheckAccessible(GetRendererAccessible());
607 }
608 
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationValueChanged)609 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
610                        TestNotificationValueChanged) {
611   LoadInitialAccessibilityTreeFromHtml(
612       "<body><input type='text' value='old value'/></body>");
613 
614   // Check the browser's copy of the renderer accessibility tree.
615   AccessibleChecker text_field_checker(std::wstring(), ROLE_SYSTEM_TEXT,
616                                        L"old value");
617   text_field_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
618   AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION,
619                                  std::wstring());
620   AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
621                                      std::wstring());
622   body_checker.AppendExpectedChild(&text_field_checker);
623   document_checker.AppendExpectedChild(&body_checker);
624   document_checker.CheckAccessible(GetRendererAccessible());
625 
626   // Set the value of the text control
627   scoped_ptr<AccessibilityNotificationWaiter> waiter(
628       new AccessibilityNotificationWaiter(
629           shell(), AccessibilityModeComplete,
630           ui::AX_EVENT_VALUE_CHANGED));
631   ExecuteScript(L"document.body.children[0].value='new value'");
632   waiter->WaitForNotification();
633 
634   // Check that the accessibility tree of the browser has been updated.
635   text_field_checker.SetExpectedValue(L"new value");
636   document_checker.CheckAccessible(GetRendererAccessible());
637 }
638 
639 // This test verifies that the web content's accessibility tree is a
640 // descendant of the main browser window's accessibility tree, so that
641 // tools like AccExplorer32 or AccProbe can be used to examine Chrome's
642 // accessibility support.
643 //
644 // If you made a change and this test now fails, check that the NativeViewHost
645 // that wraps the tab contents returns the IAccessible implementation
646 // provided by RenderWidgetHostViewWin in GetNativeViewAccessible().
647 // flaky: http://crbug.com/402190
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,DISABLED_ContainsRendererAccessibilityTree)648 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
649                        DISABLED_ContainsRendererAccessibilityTree) {
650   LoadInitialAccessibilityTreeFromHtml(
651       "<html><head><title>MyDocument</title></head>"
652       "<body>Content</body></html>");
653 
654   // Get the accessibility object for the window tree host.
655   aura::Window* window = shell()->window();
656   CHECK(window);
657   aura::WindowTreeHost* window_tree_host = window->GetHost();
658   CHECK(window_tree_host);
659   HWND hwnd = window_tree_host->GetAcceleratedWidget();
660   CHECK(hwnd);
661   base::win::ScopedComPtr<IAccessible> browser_accessible;
662   HRESULT hr = AccessibleObjectFromWindow(
663       hwnd,
664       OBJID_WINDOW,
665       IID_IAccessible,
666       reinterpret_cast<void**>(browser_accessible.Receive()));
667   ASSERT_EQ(S_OK, hr);
668 
669   bool found = false;
670   RecursiveFindNodeInAccessibilityTree(
671       browser_accessible.get(), ROLE_SYSTEM_DOCUMENT, L"MyDocument", 0, &found);
672   ASSERT_EQ(found, true);
673 }
674 
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,SupportsISimpleDOM)675 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
676                        SupportsISimpleDOM) {
677   LoadInitialAccessibilityTreeFromHtml(
678       "<body><input type='checkbox' /></body>");
679 
680   // Get the IAccessible object for the document.
681   base::win::ScopedComPtr<IAccessible> document_accessible(
682       GetRendererAccessible());
683   ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
684 
685   // Get the ISimpleDOM object for the document.
686   base::win::ScopedComPtr<IServiceProvider> service_provider;
687   HRESULT hr = static_cast<IAccessible*>(document_accessible)->QueryInterface(
688       service_provider.Receive());
689   ASSERT_EQ(S_OK, hr);
690   const GUID refguid = {0x0c539790, 0x12e4, 0x11cf,
691                         0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8};
692   base::win::ScopedComPtr<ISimpleDOMNode> document_isimpledomnode;
693   hr = static_cast<IServiceProvider *>(service_provider)->QueryService(
694       refguid, IID_ISimpleDOMNode,
695       reinterpret_cast<void**>(document_isimpledomnode.Receive()));
696   ASSERT_EQ(S_OK, hr);
697 
698   base::win::ScopedBstr node_name;
699   short name_space_id;  // NOLINT
700   base::win::ScopedBstr node_value;
701   unsigned int num_children;
702   unsigned int unique_id;
703   unsigned short node_type;  // NOLINT
704   hr = document_isimpledomnode->get_nodeInfo(
705       node_name.Receive(), &name_space_id, node_value.Receive(), &num_children,
706       &unique_id, &node_type);
707   ASSERT_EQ(S_OK, hr);
708   EXPECT_EQ(NODETYPE_DOCUMENT, node_type);
709   EXPECT_EQ(1, num_children);
710   node_name.Reset();
711   node_value.Reset();
712 
713   base::win::ScopedComPtr<ISimpleDOMNode> body_isimpledomnode;
714   hr = document_isimpledomnode->get_firstChild(
715       body_isimpledomnode.Receive());
716   ASSERT_EQ(S_OK, hr);
717   hr = body_isimpledomnode->get_nodeInfo(
718       node_name.Receive(), &name_space_id, node_value.Receive(), &num_children,
719       &unique_id, &node_type);
720   ASSERT_EQ(S_OK, hr);
721   EXPECT_EQ(L"body", std::wstring(node_name, node_name.Length()));
722   EXPECT_EQ(NODETYPE_ELEMENT, node_type);
723   EXPECT_EQ(1, num_children);
724   node_name.Reset();
725   node_value.Reset();
726 
727   base::win::ScopedComPtr<ISimpleDOMNode> checkbox_isimpledomnode;
728   hr = body_isimpledomnode->get_firstChild(
729       checkbox_isimpledomnode.Receive());
730   ASSERT_EQ(S_OK, hr);
731   hr = checkbox_isimpledomnode->get_nodeInfo(
732       node_name.Receive(), &name_space_id, node_value.Receive(), &num_children,
733       &unique_id, &node_type);
734   ASSERT_EQ(S_OK, hr);
735   EXPECT_EQ(L"input", std::wstring(node_name, node_name.Length()));
736   EXPECT_EQ(NODETYPE_ELEMENT, node_type);
737   EXPECT_EQ(0, num_children);
738 }
739 
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestRoleGroup)740 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestRoleGroup) {
741   LoadInitialAccessibilityTreeFromHtml(
742       "<fieldset></fieldset><div role=group></div>");
743 
744   // Check the browser's copy of the renderer accessibility tree.
745   AccessibleChecker grouping1_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
746                                       std::wstring());
747   AccessibleChecker grouping2_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
748                                       std::wstring());
749   AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
750                                      std::wstring());
751   document_checker.AppendExpectedChild(&grouping1_checker);
752   document_checker.AppendExpectedChild(&grouping2_checker);
753   document_checker.CheckAccessible(GetRendererAccessible());
754 }
755 
756 }  // namespace content
757