• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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 <atlbase.h>
6 #include <vector>
7 
8 #include "base/win/scoped_comptr.h"
9 #include "chrome/browser/automation/ui_controls.h"
10 #include "chrome/browser/renderer_host/render_widget_host_view_win.h"
11 #include "chrome/browser/ui/browser.h"
12 #include "chrome/browser/ui/browser_window.h"
13 #include "chrome/test/in_process_browser_test.h"
14 #include "chrome/test/ui_test_utils.h"
15 #include "content/browser/renderer_host/render_view_host.h"
16 #include "content/browser/tab_contents/tab_contents.h"
17 #include "content/common/notification_type.h"
18 #include "ia2_api_all.h"  // Generated    NOLINT
19 #include "ISimpleDOMNode.h"  // Generated   NOLINT
20 
21 using std::auto_ptr;
22 using std::vector;
23 using std::wstring;
24 
25 namespace {
26 
27 class AccessibilityWinBrowserTest : public InProcessBrowserTest {
28  public:
AccessibilityWinBrowserTest()29   AccessibilityWinBrowserTest() {}
30 
31   // InProcessBrowserTest
32   void SetUpInProcessBrowserTestFixture();
33 
34  protected:
35   IAccessible* GetRendererAccessible();
36   void ExecuteScript(wstring script);
37 };
38 
SetUpInProcessBrowserTestFixture()39 void AccessibilityWinBrowserTest::SetUpInProcessBrowserTestFixture() {
40   // If the mouse happens to be on the document then it will have the unexpected
41   // STATE_SYSTEM_HOTTRACKED state. Move it to a non-document location.
42   ui_controls::SendMouseMove(0, 0);
43 }
44 
45 class AccessibleChecker {
46  public:
47   AccessibleChecker(
48       wstring expected_name,
49       int32 expected_role,
50       wstring expected_value);
51   AccessibleChecker(
52       wstring expected_name,
53       wstring expected_role,
54       wstring expected_value);
55 
56   // Append an AccessibleChecker that verifies accessibility information for
57   // a child IAccessible. Order is important.
58   void AppendExpectedChild(AccessibleChecker* expected_child);
59 
60   // Check that the name and role of the given IAccessible instance and its
61   // descendants match the expected names and roles that this object was
62   // initialized with.
63   void CheckAccessible(IAccessible* accessible);
64 
65   // Set the expected value for this AccessibleChecker.
66   void SetExpectedValue(wstring expected_value);
67 
68   // Set the expected state for this AccessibleChecker.
69   void SetExpectedState(LONG expected_state);
70 
71  private:
72   void CheckAccessibleName(IAccessible* accessible);
73   void CheckAccessibleRole(IAccessible* accessible);
74   void CheckAccessibleValue(IAccessible* accessible);
75   void CheckAccessibleState(IAccessible* accessible);
76   void CheckAccessibleChildren(IAccessible* accessible);
77 
78  private:
79   typedef vector<AccessibleChecker*> AccessibleCheckerVector;
80 
81   // Expected accessible name. Checked against IAccessible::get_accName.
82   wstring name_;
83 
84   // Expected accessible role. Checked against IAccessible::get_accRole.
85   CComVariant role_;
86 
87   // Expected accessible value. Checked against IAccessible::get_accValue.
88   wstring value_;
89 
90   // Expected accessible state. Checked against IAccessible::get_accState.
91   LONG state_;
92 
93   // Expected accessible children. Checked using IAccessible::get_accChildCount
94   // and ::AccessibleChildren.
95   AccessibleCheckerVector children_;
96 };
97 
CreateI4Variant(LONG value)98 VARIANT CreateI4Variant(LONG value) {
99   VARIANT variant = {0};
100 
101   V_VT(&variant) = VT_I4;
102   V_I4(&variant) = value;
103 
104   return variant;
105 }
106 
GetAccessibleFromResultVariant(IAccessible * parent,VARIANT * var)107 IAccessible* GetAccessibleFromResultVariant(IAccessible* parent, VARIANT *var) {
108   switch (V_VT(var)) {
109     case VT_DISPATCH:
110       return CComQIPtr<IAccessible>(V_DISPATCH(var)).Detach();
111       break;
112 
113     case VT_I4: {
114       CComPtr<IDispatch> dispatch;
115       HRESULT hr = parent->get_accChild(CreateI4Variant(V_I4(var)), &dispatch);
116       EXPECT_TRUE(SUCCEEDED(hr));
117       return CComQIPtr<IAccessible>(dispatch).Detach();
118       break;
119     }
120   }
121 
122   return NULL;
123 }
124 
QueryIAccessible2(IAccessible * accessible,IAccessible2 ** accessible2)125 HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2) {
126   // TODO(ctguil): For some reason querying the IAccessible2 interface from
127   // IAccessible fails.
128   base::win::ScopedComPtr<IServiceProvider> service_provider;
129   HRESULT hr = accessible->QueryInterface(service_provider.Receive());
130   if (FAILED(hr))
131     return hr;
132 
133   hr = service_provider->QueryService(IID_IAccessible2, accessible2);
134   return hr;
135 }
136 
137 // Sets result to true if the child is located in the parent's tree. An
138 // exhustive search is perform here because we determine equality using
139 // IAccessible2::get_unique_id which is only supported by the child node.
AccessibleContainsAccessible(IAccessible * parent,IAccessible2 * child,bool * result)140 void AccessibleContainsAccessible(
141     IAccessible* parent, IAccessible2* child, bool* result) {
142   vector<base::win::ScopedComPtr<IAccessible>> accessible_list;
143   accessible_list.push_back(base::win::ScopedComPtr<IAccessible>(parent));
144 
145   LONG unique_id;
146   HRESULT hr = child->get_uniqueID(&unique_id);
147   ASSERT_EQ(S_OK, hr);
148   *result = false;
149 
150   while (accessible_list.size()) {
151     base::win::ScopedComPtr<IAccessible> accessible = accessible_list.back();
152     accessible_list.pop_back();
153 
154     base::win::ScopedComPtr<IAccessible2> accessible2;
155     hr = QueryIAccessible2(accessible, accessible2.Receive());
156     if (SUCCEEDED(hr)) {
157       LONG child_id;
158       accessible2->get_uniqueID(&child_id);
159       if (child_id == unique_id) {
160         *result = true;
161         break;
162       }
163     }
164 
165     LONG child_count;
166     hr = accessible->get_accChildCount(&child_count);
167     ASSERT_EQ(S_OK, hr);
168     if (child_count == 0)
169       continue;
170 
171     auto_ptr<VARIANT> child_array(new VARIANT[child_count]);
172     LONG obtained_count = 0;
173     hr = AccessibleChildren(
174         accessible, 0, child_count, child_array.get(), &obtained_count);
175     ASSERT_EQ(S_OK, hr);
176     ASSERT_EQ(child_count, obtained_count);
177 
178     for (int index = 0; index < obtained_count; index++) {
179       base::win::ScopedComPtr<IAccessible> child_accessible(
180         GetAccessibleFromResultVariant(accessible, &child_array.get()[index]));
181       if (child_accessible.get()) {
182         accessible_list.push_back(
183             base::win::ScopedComPtr<IAccessible>(child_accessible));
184       }
185     }
186   }
187 }
188 
189 // Retrieve the MSAA client accessibility object for the Render Widget Host View
190 // of the selected tab.
191 IAccessible*
GetRendererAccessible()192 AccessibilityWinBrowserTest::GetRendererAccessible() {
193   HWND hwnd_render_widget_host_view =
194       browser()->GetSelectedTabContents()->GetRenderWidgetHostView()->
195           GetNativeView();
196 
197   // Invoke windows screen reader detection by sending the WM_GETOBJECT message
198   // with kIdCustom as the LPARAM.
199   const int32 kIdCustom = 1;
200   SendMessage(
201       hwnd_render_widget_host_view, WM_GETOBJECT, OBJID_CLIENT, kIdCustom);
202 
203   IAccessible* accessible;
204   HRESULT hr = AccessibleObjectFromWindow(
205       hwnd_render_widget_host_view, OBJID_CLIENT,
206       IID_IAccessible, reinterpret_cast<void**>(&accessible));
207   EXPECT_EQ(S_OK, hr);
208   EXPECT_NE(accessible, reinterpret_cast<IAccessible*>(NULL));
209 
210   return accessible;
211 }
212 
ExecuteScript(wstring script)213 void AccessibilityWinBrowserTest::ExecuteScript(wstring script) {
214   browser()->GetSelectedTabContents()->render_view_host()->
215       ExecuteJavascriptInWebFrame(L"", script);
216 }
217 
AccessibleChecker(wstring expected_name,int32 expected_role,wstring expected_value)218 AccessibleChecker::AccessibleChecker(
219     wstring expected_name, int32 expected_role, wstring expected_value) :
220     name_(expected_name),
221     role_(expected_role),
222     value_(expected_value),
223     state_(-1) {
224 }
225 
AccessibleChecker(wstring expected_name,wstring expected_role,wstring expected_value)226 AccessibleChecker::AccessibleChecker(
227     wstring expected_name, wstring expected_role, wstring expected_value) :
228     name_(expected_name),
229     role_(expected_role.c_str()),
230     value_(expected_value),
231     state_(-1) {
232 }
233 
AppendExpectedChild(AccessibleChecker * expected_child)234 void AccessibleChecker::AppendExpectedChild(
235     AccessibleChecker* expected_child) {
236   children_.push_back(expected_child);
237 }
238 
CheckAccessible(IAccessible * accessible)239 void AccessibleChecker::CheckAccessible(IAccessible* accessible) {
240   CheckAccessibleName(accessible);
241   CheckAccessibleRole(accessible);
242   CheckAccessibleValue(accessible);
243   CheckAccessibleState(accessible);
244   CheckAccessibleChildren(accessible);
245 }
246 
SetExpectedValue(wstring expected_value)247 void AccessibleChecker::SetExpectedValue(wstring expected_value) {
248   value_ = expected_value;
249 }
250 
SetExpectedState(LONG expected_state)251 void AccessibleChecker::SetExpectedState(LONG expected_state) {
252   state_ = expected_state;
253 }
254 
CheckAccessibleName(IAccessible * accessible)255 void AccessibleChecker::CheckAccessibleName(IAccessible* accessible) {
256   CComBSTR name;
257   HRESULT hr =
258       accessible->get_accName(CreateI4Variant(CHILDID_SELF), &name);
259 
260   if (name_.empty()) {
261     // If the object doesn't have name S_FALSE should be returned.
262     EXPECT_EQ(hr, S_FALSE);
263   } else {
264     // Test that the correct string was returned.
265     EXPECT_EQ(S_OK, hr);
266     EXPECT_STREQ(name_.c_str(),
267                  wstring(name.m_str, SysStringLen(name)).c_str());
268   }
269 }
270 
CheckAccessibleRole(IAccessible * accessible)271 void AccessibleChecker::CheckAccessibleRole(IAccessible* accessible) {
272   VARIANT var_role = {0};
273   HRESULT hr =
274       accessible->get_accRole(CreateI4Variant(CHILDID_SELF), &var_role);
275   ASSERT_EQ(S_OK, hr);
276   EXPECT_TRUE(role_ == var_role);
277 }
278 
CheckAccessibleValue(IAccessible * accessible)279 void AccessibleChecker::CheckAccessibleValue(IAccessible* accessible) {
280   CComBSTR value;
281   HRESULT hr =
282       accessible->get_accValue(CreateI4Variant(CHILDID_SELF), &value);
283   EXPECT_EQ(S_OK, hr);
284 
285   // Test that the correct string was returned.
286   EXPECT_STREQ(value_.c_str(),
287                wstring(value.m_str, SysStringLen(value)).c_str());
288 }
289 
CheckAccessibleState(IAccessible * accessible)290 void AccessibleChecker::CheckAccessibleState(IAccessible* accessible) {
291   if (state_ < 0)
292     return;
293 
294   VARIANT var_state = {0};
295   HRESULT hr =
296       accessible->get_accState(CreateI4Variant(CHILDID_SELF), &var_state);
297   EXPECT_EQ(S_OK, hr);
298   ASSERT_EQ(VT_I4, V_VT(&var_state));
299   EXPECT_EQ(state_, V_I4(&var_state));
300 }
301 
CheckAccessibleChildren(IAccessible * parent)302 void AccessibleChecker::CheckAccessibleChildren(IAccessible* parent) {
303   LONG child_count = 0;
304   HRESULT hr = parent->get_accChildCount(&child_count);
305   EXPECT_EQ(S_OK, hr);
306   ASSERT_EQ(child_count, children_.size());
307 
308   auto_ptr<VARIANT> child_array(new VARIANT[child_count]);
309   LONG obtained_count = 0;
310   hr = AccessibleChildren(parent, 0, child_count,
311                           child_array.get(), &obtained_count);
312   ASSERT_EQ(S_OK, hr);
313   ASSERT_EQ(child_count, obtained_count);
314 
315   VARIANT* child = child_array.get();
316   for (AccessibleCheckerVector::iterator child_checker = children_.begin();
317        child_checker != children_.end();
318        ++child_checker, ++child) {
319     base::win::ScopedComPtr<IAccessible> child_accessible;
320     child_accessible.Attach(GetAccessibleFromResultVariant(parent, child));
321     ASSERT_TRUE(child_accessible.get());
322     (*child_checker)->CheckAccessible(child_accessible);
323   }
324 }
325 
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestRendererAccessibilityTree)326 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
327                        TestRendererAccessibilityTree) {
328   // The initial accessible returned should have state STATE_SYSTEM_BUSY while
329   // the accessibility tree is being requested from the renderer.
330   AccessibleChecker document1_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
331   document1_checker.SetExpectedState(
332       STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED |
333       STATE_SYSTEM_BUSY);
334   document1_checker.CheckAccessible(GetRendererAccessible());
335 
336   // Wait for the initial accessibility tree to load. Busy state should clear.
337   ui_test_utils::WaitForNotification(
338       NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
339   document1_checker.SetExpectedState(
340       STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED);
341   document1_checker.CheckAccessible(GetRendererAccessible());
342 
343   GURL tree_url(
344       "data:text/html,<html><head><title>Accessibility Win Test</title></head>"
345       "<body><input type='button' value='push' /><input type='checkbox' />"
346       "</body></html>");
347   browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
348   ui_test_utils::WaitForNotification(
349       NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
350 
351   // Check the browser's copy of the renderer accessibility tree.
352   AccessibleChecker button_checker(L"push", ROLE_SYSTEM_PUSHBUTTON, L"push");
353   AccessibleChecker checkbox_checker(L"", ROLE_SYSTEM_CHECKBUTTON, L"");
354   AccessibleChecker body_checker(L"", L"body", L"");
355   AccessibleChecker document2_checker(
356     L"Accessibility Win Test", ROLE_SYSTEM_DOCUMENT, L"");
357   body_checker.AppendExpectedChild(&button_checker);
358   body_checker.AppendExpectedChild(&checkbox_checker);
359   document2_checker.AppendExpectedChild(&body_checker);
360   document2_checker.CheckAccessible(GetRendererAccessible());
361 
362   // Check that document accessible has a parent accessible.
363   base::win::ScopedComPtr<IAccessible> document_accessible(
364       GetRendererAccessible());
365   ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
366   base::win::ScopedComPtr<IDispatch> parent_dispatch;
367   HRESULT hr = document_accessible->get_accParent(parent_dispatch.Receive());
368   EXPECT_EQ(S_OK, hr);
369   EXPECT_NE(parent_dispatch, reinterpret_cast<IDispatch*>(NULL));
370 
371   // Navigate to another page.
372   GURL about_url("about:");
373   ui_test_utils::NavigateToURL(browser(), about_url);
374 
375   // Verify that the IAccessible reference still points to a valid object and
376   // that calls to its methods fail since the tree is no longer valid after
377   // the page navagation.
378   CComBSTR name;
379   hr = document_accessible->get_accName(CreateI4Variant(CHILDID_SELF), &name);
380   ASSERT_EQ(E_FAIL, hr);
381 }
382 
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationActiveDescendantChanged)383 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
384                        TestNotificationActiveDescendantChanged) {
385   GURL tree_url("data:text/html,<ul tabindex='-1' role='radiogroup'><li id='li'"
386       ">li</li></ul>");
387   browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
388   GetRendererAccessible();
389   ui_test_utils::WaitForNotification(
390       NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
391 
392   // Check the browser's copy of the renderer accessibility tree.
393   AccessibleChecker list_marker_checker(L"", ROLE_SYSTEM_LISTITEM, L"\x2022");
394   AccessibleChecker static_text_checker(L"li", ROLE_SYSTEM_TEXT, L"");
395   AccessibleChecker list_item_checker(L"", ROLE_SYSTEM_LISTITEM, L"");
396   list_item_checker.SetExpectedState(
397       STATE_SYSTEM_READONLY);
398   AccessibleChecker radio_group_checker(L"", ROLE_SYSTEM_GROUPING, L"");
399   radio_group_checker.SetExpectedState(
400       STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY);
401   AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
402   list_item_checker.AppendExpectedChild(&list_marker_checker);
403   list_item_checker.AppendExpectedChild(&static_text_checker);
404   radio_group_checker.AppendExpectedChild(&list_item_checker);
405   document_checker.AppendExpectedChild(&radio_group_checker);
406   document_checker.CheckAccessible(GetRendererAccessible());
407 
408   // Set focus to the radio group.
409   ExecuteScript(L"document.body.children[0].focus()");
410   ui_test_utils::WaitForNotification(
411       NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
412 
413   // Check that the accessibility tree of the browser has been updated.
414   radio_group_checker.SetExpectedState(
415       STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSED);
416   document_checker.CheckAccessible(GetRendererAccessible());
417 
418   // Set the active descendant of the radio group
419   ExecuteScript(
420       L"document.body.children[0].setAttribute('aria-activedescendant', 'li')");
421   ui_test_utils::WaitForNotification(
422       NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
423 
424   // Check that the accessibility tree of the browser has been updated.
425   list_item_checker.SetExpectedState(
426       STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSED);
427   radio_group_checker.SetExpectedState(
428       STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY);
429   document_checker.CheckAccessible(GetRendererAccessible());
430 }
431 
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationCheckedStateChanged)432 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
433                        TestNotificationCheckedStateChanged) {
434   GURL tree_url("data:text/html,<body><input type='checkbox' /></body>");
435   browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
436   GetRendererAccessible();
437   ui_test_utils::WaitForNotification(
438       NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
439 
440   // Check the browser's copy of the renderer accessibility tree.
441   AccessibleChecker checkbox_checker(L"", ROLE_SYSTEM_CHECKBUTTON, L"");
442   checkbox_checker.SetExpectedState(
443       STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY);
444   AccessibleChecker body_checker(L"", L"body", L"");
445   AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
446   body_checker.AppendExpectedChild(&checkbox_checker);
447   document_checker.AppendExpectedChild(&body_checker);
448   document_checker.CheckAccessible(GetRendererAccessible());
449 
450   // Check the checkbox.
451   ExecuteScript(L"document.body.children[0].checked=true");
452   ui_test_utils::WaitForNotification(
453       NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
454 
455   // Check that the accessibility tree of the browser has been updated.
456   checkbox_checker.SetExpectedState(
457       STATE_SYSTEM_CHECKED | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY);
458   document_checker.CheckAccessible(GetRendererAccessible());
459 }
460 
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationChildrenChanged)461 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
462                        TestNotificationChildrenChanged) {
463   // The role attribute causes the node to be in the accessibility tree.
464   GURL tree_url(
465       "data:text/html,<body role=group></body>");
466   browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
467   GetRendererAccessible();
468   ui_test_utils::WaitForNotification(
469       NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
470 
471   // Check the browser's copy of the renderer accessibility tree.
472   AccessibleChecker body_checker(L"", L"body", L"");
473   AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
474   document_checker.AppendExpectedChild(&body_checker);
475   document_checker.CheckAccessible(GetRendererAccessible());
476 
477   // Change the children of the document body.
478   ExecuteScript(L"document.body.innerHTML='<b>new text</b>'");
479   ui_test_utils::WaitForNotification(
480       NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
481 
482   // Check that the accessibility tree of the browser has been updated.
483   AccessibleChecker text_checker(L"new text", ROLE_SYSTEM_TEXT, L"");
484   body_checker.AppendExpectedChild(&text_checker);
485   document_checker.CheckAccessible(GetRendererAccessible());
486 }
487 
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationChildrenChanged2)488 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
489                        TestNotificationChildrenChanged2) {
490   // The role attribute causes the node to be in the accessibility tree.
491   GURL tree_url(
492       "data:text/html,<div role=group style='visibility: hidden'>text"
493       "</div>");
494   browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
495   GetRendererAccessible();
496   ui_test_utils::WaitForNotification(
497       NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
498 
499   // Check the accessible tree of the browser.
500   AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
501   document_checker.CheckAccessible(GetRendererAccessible());
502 
503   // Change the children of the document body.
504   ExecuteScript(L"document.body.children[0].style.visibility='visible'");
505   ui_test_utils::WaitForNotification(
506       NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
507 
508   // Check that the accessibility tree of the browser has been updated.
509   AccessibleChecker static_text_checker(L"text", ROLE_SYSTEM_TEXT, L"");
510   AccessibleChecker div_checker(L"", L"div", L"");
511   document_checker.AppendExpectedChild(&div_checker);
512   div_checker.AppendExpectedChild(&static_text_checker);
513   document_checker.CheckAccessible(GetRendererAccessible());
514 }
515 
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationFocusChanged)516 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
517                        TestNotificationFocusChanged) {
518   // The role attribute causes the node to be in the accessibility tree.
519   GURL tree_url(
520       "data:text/html,<div role=group tabindex='-1'></div>");
521   browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
522   GetRendererAccessible();
523   ui_test_utils::WaitForNotification(
524       NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
525 
526   // Check the browser's copy of the renderer accessibility tree.
527   AccessibleChecker div_checker(L"", L"div", L"");
528   div_checker.SetExpectedState(
529       STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_OFFSCREEN | STATE_SYSTEM_READONLY);
530   AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
531   document_checker.AppendExpectedChild(&div_checker);
532   document_checker.CheckAccessible(GetRendererAccessible());
533 
534   // Focus the div in the document
535   ExecuteScript(L"document.body.children[0].focus()");
536   ui_test_utils::WaitForNotification(
537       NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
538 
539   // Check that the accessibility tree of the browser has been updated.
540   div_checker.SetExpectedState(
541       STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSED);
542   document_checker.CheckAccessible(GetRendererAccessible());
543 
544   // Focus the document accessible. This will un-focus the current node.
545   base::win::ScopedComPtr<IAccessible> document_accessible(
546       GetRendererAccessible());
547   ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
548   HRESULT hr = document_accessible->accSelect(
549     SELFLAG_TAKEFOCUS, CreateI4Variant(CHILDID_SELF));
550   ASSERT_EQ(S_OK, hr);
551   ui_test_utils::WaitForNotification(
552       NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
553 
554   // Check that the accessibility tree of the browser has been updated.
555   div_checker.SetExpectedState(
556       STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY);
557   document_checker.CheckAccessible(GetRendererAccessible());
558 }
559 
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationValueChanged)560 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
561                        TestNotificationValueChanged) {
562   GURL tree_url("data:text/html,<body><input type='text' value='old value'/>"
563       "</body>");
564   browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
565   GetRendererAccessible();
566   ui_test_utils::WaitForNotification(
567       NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
568 
569   // Check the browser's copy of the renderer accessibility tree.
570 
571   AccessibleChecker text_field_div_checker(L"", L"div", L"");
572   AccessibleChecker text_field_checker(L"", ROLE_SYSTEM_TEXT, L"old value");
573   text_field_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
574   AccessibleChecker body_checker(L"", L"body", L"");
575   AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
576   text_field_checker.AppendExpectedChild(&text_field_div_checker);
577   body_checker.AppendExpectedChild(&text_field_checker);
578   document_checker.AppendExpectedChild(&body_checker);
579   document_checker.CheckAccessible(GetRendererAccessible());
580 
581   // Set the value of the text control
582   ExecuteScript(L"document.body.children[0].value='new value'");
583   ui_test_utils::WaitForNotification(
584       NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
585 
586   // Check that the accessibility tree of the browser has been updated.
587   text_field_checker.SetExpectedValue(L"new value");
588   document_checker.CheckAccessible(GetRendererAccessible());
589 }
590 
591 // FAILS crbug.com/54220
592 // This test verifies that browser-side cache of the renderer accessibility
593 // tree is reachable from the browser's tree. Tools that analyze windows
594 // accessibility trees like AccExplorer32 should be able to drill into the
595 // cached renderer accessibility tree.
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,DISABLED_ContainsRendererAccessibilityTree)596 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
597                        DISABLED_ContainsRendererAccessibilityTree) {
598   GURL tree_url("data:text/html,<body><input type='checkbox' /></body>");
599   browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
600   GetRendererAccessible();
601   ui_test_utils::WaitForNotification(
602       NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
603 
604   // Get the accessibility object for the browser window.
605   HWND browser_hwnd = browser()->window()->GetNativeHandle();
606   base::win::ScopedComPtr<IAccessible> browser_accessible;
607   HRESULT hr = AccessibleObjectFromWindow(
608       browser_hwnd,
609       OBJID_WINDOW,
610       IID_IAccessible,
611       reinterpret_cast<void**>(browser_accessible.Receive()));
612   ASSERT_EQ(S_OK, hr);
613 
614   // Get the accessibility object for the renderer client document.
615   base::win::ScopedComPtr<IAccessible> document_accessible(
616       GetRendererAccessible());
617   ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
618   base::win::ScopedComPtr<IAccessible2> document_accessible2;
619   hr = QueryIAccessible2(document_accessible, document_accessible2.Receive());
620   ASSERT_EQ(S_OK, hr);
621 
622   // TODO(ctguil): Pointer comparison of retrieved IAccessible pointers dosen't
623   // seem to work for here. Perhaps make IAccessible2 available in views to make
624   // unique id comparison available.
625   bool found = false;
626   base::win::ScopedComPtr<IAccessible> parent = document_accessible;
627   while (parent.get()) {
628     base::win::ScopedComPtr<IDispatch> parent_dispatch;
629     hr = parent->get_accParent(parent_dispatch.Receive());
630     ASSERT_TRUE(SUCCEEDED(hr));
631     if (!parent_dispatch.get()) {
632       ASSERT_EQ(hr, S_FALSE);
633       break;
634     }
635 
636     parent.Release();
637     hr = parent_dispatch.QueryInterface(parent.Receive());
638     ASSERT_EQ(S_OK, hr);
639 
640     if (parent.get() == browser_accessible.get()) {
641       found = true;
642       break;
643     }
644   }
645 
646   // If pointer comparison fails resort to the exhuasive search that can use
647   // IAccessible2::get_unique_id for equality comparison.
648   if (!found) {
649     AccessibleContainsAccessible(
650         browser_accessible, document_accessible2, &found);
651   }
652 
653   ASSERT_EQ(found, true);
654 }
655 
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,SupportsISimpleDOM)656 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
657                        SupportsISimpleDOM) {
658   GURL tree_url("data:text/html,<body><input type='checkbox' /></body>");
659   browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
660   GetRendererAccessible();
661   ui_test_utils::WaitForNotification(
662       NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
663 
664   // Get the IAccessible object for the document.
665   base::win::ScopedComPtr<IAccessible> document_accessible(
666       GetRendererAccessible());
667   ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
668 
669   // Get the ISimpleDOM object for the document.
670   base::win::ScopedComPtr<IServiceProvider> service_provider;
671   HRESULT hr = static_cast<IAccessible*>(document_accessible)->QueryInterface(
672       service_provider.Receive());
673   ASSERT_EQ(S_OK, hr);
674   const GUID refguid = {0x0c539790, 0x12e4, 0x11cf,
675                         0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8};
676   base::win::ScopedComPtr<ISimpleDOMNode> document_isimpledomnode;
677   hr = static_cast<IServiceProvider *>(service_provider)->QueryService(
678       refguid, IID_ISimpleDOMNode,
679       reinterpret_cast<void**>(document_isimpledomnode.Receive()));
680   ASSERT_EQ(S_OK, hr);
681 
682   BSTR node_name;
683   short name_space_id;  // NOLINT
684   BSTR node_value;
685   unsigned int num_children;
686   unsigned int unique_id;
687   unsigned short node_type;  // NOLINT
688   hr = document_isimpledomnode->get_nodeInfo(
689       &node_name, &name_space_id, &node_value, &num_children, &unique_id,
690       &node_type);
691   ASSERT_EQ(S_OK, hr);
692   EXPECT_EQ(NODETYPE_DOCUMENT, node_type);
693   EXPECT_EQ(1, num_children);
694 
695   base::win::ScopedComPtr<ISimpleDOMNode> body_isimpledomnode;
696   hr = document_isimpledomnode->get_firstChild(
697       body_isimpledomnode.Receive());
698   ASSERT_EQ(S_OK, hr);
699   hr = body_isimpledomnode->get_nodeInfo(
700       &node_name, &name_space_id, &node_value, &num_children, &unique_id,
701       &node_type);
702   ASSERT_EQ(S_OK, hr);
703   EXPECT_STREQ(L"body", wstring(node_name, SysStringLen(node_name)).c_str());
704   EXPECT_EQ(NODETYPE_ELEMENT, node_type);
705   EXPECT_EQ(1, num_children);
706 
707   base::win::ScopedComPtr<ISimpleDOMNode> checkbox_isimpledomnode;
708   hr = body_isimpledomnode->get_firstChild(
709       checkbox_isimpledomnode.Receive());
710   ASSERT_EQ(S_OK, hr);
711   hr = checkbox_isimpledomnode->get_nodeInfo(
712       &node_name, &name_space_id, &node_value, &num_children, &unique_id,
713       &node_type);
714   ASSERT_EQ(S_OK, hr);
715   EXPECT_STREQ(L"input", wstring(node_name, SysStringLen(node_name)).c_str());
716   EXPECT_EQ(NODETYPE_ELEMENT, node_type);
717   EXPECT_EQ(0, num_children);
718 }
719 }  // namespace.
720