Upstream version 6.35.121.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / accessibility / accessibility_event_router_views_unittest.cc
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 <string>
6
7 #include "base/message_loop/message_loop.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/accessibility/accessibility_extension_api.h"
11 #include "chrome/browser/accessibility/accessibility_extension_api_constants.h"
12 #include "chrome/browser/ui/views/accessibility/accessibility_event_router_views.h"
13 #include "chrome/test/base/testing_profile.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "ui/accessibility/ax_enums.h"
16 #include "ui/accessibility/ax_view_state.h"
17 #include "ui/base/models/simple_menu_model.h"
18 #include "ui/views/controls/button/label_button.h"
19 #include "ui/views/controls/label.h"
20 #include "ui/views/controls/menu/menu_item_view.h"
21 #include "ui/views/controls/menu/menu_runner.h"
22 #include "ui/views/controls/menu/submenu_view.h"
23 #include "ui/views/layout/grid_layout.h"
24 #include "ui/views/test/test_views_delegate.h"
25 #include "ui/views/widget/native_widget.h"
26 #include "ui/views/widget/root_view.h"
27 #include "ui/views/widget/widget.h"
28 #include "ui/views/widget/widget_delegate.h"
29
30 #if defined(OS_WIN)
31 #include "ui/base/win/scoped_ole_initializer.h"
32 #endif
33
34 #if defined(USE_AURA)
35 #include "ui/aura/test/aura_test_helper.h"
36 #include "ui/aura/window_event_dispatcher.h"
37 #include "ui/compositor/test/context_factories_for_test.h"
38 #endif
39
40 using base::ASCIIToUTF16;
41
42 class AccessibilityViewsDelegate : public views::TestViewsDelegate {
43  public:
44   AccessibilityViewsDelegate() {}
45   virtual ~AccessibilityViewsDelegate() {}
46
47   // Overridden from views::TestViewsDelegate:
48   virtual void NotifyAccessibilityEvent(
49       views::View* view, ui::AXEvent event_type) OVERRIDE {
50     AccessibilityEventRouterViews::GetInstance()->HandleAccessibilityEvent(
51         view, event_type);
52   }
53
54   DISALLOW_COPY_AND_ASSIGN(AccessibilityViewsDelegate);
55 };
56
57 class AccessibilityWindowDelegate : public views::WidgetDelegate {
58  public:
59   explicit AccessibilityWindowDelegate(views::View* contents)
60       : contents_(contents) { }
61
62   // Overridden from views::WidgetDelegate:
63   virtual void DeleteDelegate() OVERRIDE { delete this; }
64   virtual views::View* GetContentsView() OVERRIDE { return contents_; }
65   virtual const views::Widget* GetWidget() const OVERRIDE {
66     return contents_->GetWidget();
67   }
68   virtual views::Widget* GetWidget() OVERRIDE { return contents_->GetWidget(); }
69
70  private:
71   views::View* contents_;
72
73   DISALLOW_COPY_AND_ASSIGN(AccessibilityWindowDelegate);
74 };
75
76 class ViewWithNameAndRole : public views::View {
77  public:
78   explicit ViewWithNameAndRole(const base::string16& name,
79                                ui::AXRole role)
80       : name_(name),
81         role_(role) {
82   }
83
84   virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE {
85     views::View::GetAccessibleState(state);
86     state->name = name_;
87     state->role = role_;
88   }
89
90   void set_name(const base::string16& name) { name_ = name; }
91
92  private:
93   base::string16 name_;
94   ui::AXRole role_;
95   DISALLOW_COPY_AND_ASSIGN(ViewWithNameAndRole);
96 };
97
98 class AccessibilityEventRouterViewsTest
99     : public testing::Test {
100  public:
101   AccessibilityEventRouterViewsTest() : control_event_count_(0) {
102   }
103
104   virtual void SetUp() {
105 #if defined(OS_WIN)
106     ole_initializer_.reset(new ui::ScopedOleInitializer());
107 #endif
108     views::ViewsDelegate::views_delegate = new AccessibilityViewsDelegate();
109 #if defined(USE_AURA)
110     // The ContextFactory must exist before any Compositors are created.
111     bool enable_pixel_output = false;
112     ui::InitializeContextFactoryForTests(enable_pixel_output);
113
114     aura_test_helper_.reset(new aura::test::AuraTestHelper(&message_loop_));
115     aura_test_helper_->SetUp();
116 #endif  // USE_AURA
117     EnableAccessibilityAndListenToFocusNotifications();
118   }
119
120   virtual void TearDown() {
121     ClearCallback();
122 #if defined(USE_AURA)
123     aura_test_helper_->TearDown();
124     ui::TerminateContextFactoryForTests();
125 #endif
126     delete views::ViewsDelegate::views_delegate;
127     views::ViewsDelegate::views_delegate = NULL;
128
129     // The Widget's FocusManager is deleted using DeleteSoon - this
130     // forces it to be deleted now, so we don't have any memory leaks
131     // when this method exits.
132     base::MessageLoop::current()->RunUntilIdle();
133
134 #if defined(OS_WIN)
135     ole_initializer_.reset();
136 #endif
137   }
138
139   views::Widget* CreateWindowWithContents(views::View* contents) {
140     gfx::NativeView context = NULL;
141 #if defined(USE_AURA)
142     context = aura_test_helper_->root_window();
143 #endif
144     views::Widget* widget = views::Widget::CreateWindowWithContextAndBounds(
145         new AccessibilityWindowDelegate(contents),
146         context,
147         gfx::Rect(0, 0, 500, 500));
148
149     // Create a profile and associate it with this window.
150     widget->SetNativeWindowProperty(Profile::kProfileKey, &profile_);
151
152     return widget;
153   }
154
155   void EnableAccessibilityAndListenToFocusNotifications() {
156     // Switch on accessibility event notifications.
157     ExtensionAccessibilityEventRouter* accessibility_event_router =
158         ExtensionAccessibilityEventRouter::GetInstance();
159     accessibility_event_router->SetAccessibilityEnabled(true);
160     accessibility_event_router->SetControlEventCallbackForTesting(base::Bind(
161         &AccessibilityEventRouterViewsTest::OnControlEvent,
162         base::Unretained(this)));
163   }
164
165   void ClearCallback() {
166     ExtensionAccessibilityEventRouter* accessibility_event_router =
167         ExtensionAccessibilityEventRouter::GetInstance();
168     accessibility_event_router->ClearControlEventCallback();
169   }
170
171  protected:
172   // Handle Focus event.
173   virtual void OnControlEvent(ui::AXEvent event,
174                             const AccessibilityControlInfo* info) {
175     control_event_count_++;
176     last_control_type_ = info->type();
177     last_control_name_ = info->name();
178     last_control_context_ = info->context();
179   }
180
181   base::MessageLoopForUI message_loop_;
182   int control_event_count_;
183   std::string last_control_type_;
184   std::string last_control_name_;
185   std::string last_control_context_;
186   TestingProfile profile_;
187 #if defined(OS_WIN)
188   scoped_ptr<ui::ScopedOleInitializer> ole_initializer_;
189 #endif
190 #if defined(USE_AURA)
191   scoped_ptr<aura::test::AuraTestHelper> aura_test_helper_;
192 #endif
193 };
194
195 TEST_F(AccessibilityEventRouterViewsTest, TestFocusNotification) {
196   const char kButton1ASCII[] = "Button1";
197   const char kButton2ASCII[] = "Button2";
198   const char kButton3ASCII[] = "Button3";
199   const char kButton3NewASCII[] = "Button3New";
200
201   // Create a contents view with 3 buttons.
202   views::View* contents = new views::View();
203   views::LabelButton* button1 = new views::LabelButton(
204       NULL, ASCIIToUTF16(kButton1ASCII));
205   button1->SetStyle(views::Button::STYLE_BUTTON);
206   contents->AddChildView(button1);
207   views::LabelButton* button2 = new views::LabelButton(
208       NULL, ASCIIToUTF16(kButton2ASCII));
209   button2->SetStyle(views::Button::STYLE_BUTTON);
210   contents->AddChildView(button2);
211   views::LabelButton* button3 = new views::LabelButton(
212       NULL, ASCIIToUTF16(kButton3ASCII));
213   button3->SetStyle(views::Button::STYLE_BUTTON);
214   contents->AddChildView(button3);
215
216   // Put the view in a window.
217   views::Widget* window = CreateWindowWithContents(contents);
218   window->Show();
219
220   // Set focus to the first button initially and run message loop to execute
221   // callback.
222   button1->RequestFocus();
223   base::MessageLoop::current()->RunUntilIdle();
224
225   // Change the accessible name of button3.
226   button3->SetAccessibleName(ASCIIToUTF16(kButton3NewASCII));
227
228   // Advance focus to the next button and test that we got the
229   // expected notification with the name of button 2.
230   views::FocusManager* focus_manager = contents->GetWidget()->GetFocusManager();
231   control_event_count_ = 0;
232   focus_manager->AdvanceFocus(false);
233   base::MessageLoop::current()->RunUntilIdle();
234   EXPECT_EQ(1, control_event_count_);
235   EXPECT_EQ(kButton2ASCII, last_control_name_);
236
237   // Advance to button 3. Expect the new accessible name we assigned.
238   focus_manager->AdvanceFocus(false);
239   base::MessageLoop::current()->RunUntilIdle();
240   EXPECT_EQ(2, control_event_count_);
241   EXPECT_EQ(kButton3NewASCII, last_control_name_);
242
243   // Advance to button 1 and check the notification.
244   focus_manager->AdvanceFocus(false);
245   base::MessageLoop::current()->RunUntilIdle();
246   EXPECT_EQ(3, control_event_count_);
247   EXPECT_EQ(kButton1ASCII, last_control_name_);
248
249   window->CloseNow();
250 }
251
252 TEST_F(AccessibilityEventRouterViewsTest, TestToolbarContext) {
253   const char kToolbarNameASCII[] = "MyToolbar";
254   const char kButtonNameASCII[] = "MyButton";
255
256   // Create a toolbar with a button.
257   views::View* contents = new ViewWithNameAndRole(
258       ASCIIToUTF16(kToolbarNameASCII),
259       ui::AX_ROLE_TOOLBAR);
260   views::LabelButton* button = new views::LabelButton(
261       NULL, ASCIIToUTF16(kButtonNameASCII));
262   button->SetStyle(views::Button::STYLE_BUTTON);
263   contents->AddChildView(button);
264
265   // Put the view in a window.
266   views::Widget* window = CreateWindowWithContents(contents);
267
268   // Set focus to the button.
269   control_event_count_ = 0;
270   button->RequestFocus();
271
272   base::MessageLoop::current()->RunUntilIdle();
273
274   // Test that we got the event with the expected name and context.
275   EXPECT_EQ(1, control_event_count_);
276   EXPECT_EQ(kButtonNameASCII, last_control_name_);
277   EXPECT_EQ(kToolbarNameASCII, last_control_context_);
278
279   window->CloseNow();
280 }
281
282 TEST_F(AccessibilityEventRouterViewsTest, TestAlertContext) {
283   const char kAlertTextASCII[] = "MyAlertText";
284   const char kButtonNameASCII[] = "MyButton";
285
286   // Create an alert with static text and a button, similar to an infobar.
287   views::View* contents = new ViewWithNameAndRole(
288       base::string16(),
289       ui::AX_ROLE_ALERT);
290   views::Label* label = new views::Label(ASCIIToUTF16(kAlertTextASCII));
291   contents->AddChildView(label);
292   views::LabelButton* button = new views::LabelButton(
293       NULL, ASCIIToUTF16(kButtonNameASCII));
294   button->SetStyle(views::Button::STYLE_BUTTON);
295   contents->AddChildView(button);
296
297   // Put the view in a window.
298   views::Widget* window = CreateWindowWithContents(contents);
299
300   // Set focus to the button.
301   control_event_count_ = 0;
302   button->RequestFocus();
303
304   base::MessageLoop::current()->RunUntilIdle();
305
306   // Test that we got the event with the expected name and context.
307   EXPECT_EQ(1, control_event_count_);
308   EXPECT_EQ(kButtonNameASCII, last_control_name_);
309   EXPECT_EQ(kAlertTextASCII, last_control_context_);
310
311   window->CloseNow();
312 }
313
314 TEST_F(AccessibilityEventRouterViewsTest, StateChangeAfterNotification) {
315   const char kContentsNameASCII[] = "Contents";
316   const char kOldNameASCII[] = "OldName";
317   const char kNewNameASCII[] = "NewName";
318
319   // Create a toolbar with a button.
320   views::View* contents = new ViewWithNameAndRole(
321       ASCIIToUTF16(kContentsNameASCII),
322       ui::AX_ROLE_CLIENT);
323   ViewWithNameAndRole* child = new ViewWithNameAndRole(
324       ASCIIToUTF16(kOldNameASCII),
325       ui::AX_ROLE_BUTTON);
326   child->SetFocusable(true);
327   contents->AddChildView(child);
328
329   // Put the view in a window.
330   views::Widget* window = CreateWindowWithContents(contents);
331
332   // Set focus to the child view.
333   control_event_count_ = 0;
334   child->RequestFocus();
335
336   // Change the child's name after the focus notification.
337   child->set_name(ASCIIToUTF16(kNewNameASCII));
338
339   // We shouldn't get the notification right away.
340   EXPECT_EQ(0, control_event_count_);
341
342   // Process anything in the event loop. Now we should get the notification,
343   // and it should give us the new control name, not the old one.
344   base::MessageLoop::current()->RunUntilIdle();
345   EXPECT_EQ(1, control_event_count_);
346   EXPECT_EQ(kNewNameASCII, last_control_name_);
347
348   window->CloseNow();
349 }
350
351 TEST_F(AccessibilityEventRouterViewsTest, NotificationOnDeletedObject) {
352   const char kContentsNameASCII[] = "Contents";
353   const char kNameASCII[] = "OldName";
354
355   // Create a toolbar with a button.
356   views::View* contents = new ViewWithNameAndRole(
357       ASCIIToUTF16(kContentsNameASCII),
358       ui::AX_ROLE_CLIENT);
359   ViewWithNameAndRole* child = new ViewWithNameAndRole(
360       ASCIIToUTF16(kNameASCII),
361       ui::AX_ROLE_BUTTON);
362   child->SetFocusable(true);
363   contents->AddChildView(child);
364
365   // Put the view in a window.
366   views::Widget* window = CreateWindowWithContents(contents);
367
368   // Set focus to the child view.
369   control_event_count_ = 0;
370   child->RequestFocus();
371
372   // Delete the child!
373   delete child;
374
375   // We shouldn't get the notification right away.
376   EXPECT_EQ(0, control_event_count_);
377
378   // Process anything in the event loop. We shouldn't get a notification
379   // because the view is no longer valid, and this shouldn't crash.
380   base::MessageLoop::current()->RunUntilIdle();
381   EXPECT_EQ(0, control_event_count_);
382
383   window->CloseNow();
384 }
385
386 TEST_F(AccessibilityEventRouterViewsTest, AlertsFromWindowAndControl) {
387   const char kButtonASCII[] = "Button";
388   const char* kTypeAlert = extension_accessibility_api_constants::kTypeAlert;
389   const char* kTypeWindow = extension_accessibility_api_constants::kTypeWindow;
390
391   // Create a contents view with a button.
392   views::View* contents = new views::View();
393   views::LabelButton* button = new views::LabelButton(
394       NULL, ASCIIToUTF16(kButtonASCII));
395   button->SetStyle(views::Button::STYLE_BUTTON);
396   contents->AddChildView(button);
397
398   // Put the view in a window.
399   views::Widget* window = CreateWindowWithContents(contents);
400   window->Show();
401
402   // Send an alert event from the button and let the event loop run.
403   control_event_count_ = 0;
404   button->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
405   base::MessageLoop::current()->RunUntilIdle();
406
407   EXPECT_EQ(kTypeAlert, last_control_type_);
408   EXPECT_EQ(1, control_event_count_);
409   EXPECT_EQ(kButtonASCII, last_control_name_);
410
411   // Send an alert event from the window and let the event loop run.
412   control_event_count_ = 0;
413   window->GetRootView()->NotifyAccessibilityEvent(
414       ui::AX_EVENT_ALERT, true);
415   base::MessageLoop::current()->RunUntilIdle();
416
417   EXPECT_EQ(1, control_event_count_);
418   EXPECT_EQ(kTypeWindow, last_control_type_);
419
420   window->CloseNow();
421 }
422
423 namespace {
424
425 class SimpleMenuDelegate : public ui::SimpleMenuModel::Delegate {
426  public:
427   enum {
428     IDC_MENU_ITEM_1,
429     IDC_MENU_ITEM_2,
430     IDC_MENU_INVISIBLE,
431     IDC_MENU_ITEM_3,
432   };
433
434   SimpleMenuDelegate() {}
435   virtual ~SimpleMenuDelegate() {}
436
437   views::MenuItemView* BuildMenu() {
438     menu_model_.reset(new ui::SimpleMenuModel(this));
439     menu_model_->AddItem(IDC_MENU_ITEM_1, ASCIIToUTF16("Item 1"));
440     menu_model_->AddItem(IDC_MENU_ITEM_2, ASCIIToUTF16("Item 2"));
441     menu_model_->AddSeparator(ui::NORMAL_SEPARATOR);
442     menu_model_->AddItem(IDC_MENU_INVISIBLE, ASCIIToUTF16("Invisible"));
443     menu_model_->AddSeparator(ui::NORMAL_SEPARATOR);
444     menu_model_->AddItem(IDC_MENU_ITEM_3, ASCIIToUTF16("Item 3"));
445
446     menu_runner_.reset(new views::MenuRunner(menu_model_.get()));
447     return menu_runner_->GetMenu();
448   }
449
450   virtual bool IsCommandIdChecked(int command_id) const OVERRIDE {
451     return false;
452   }
453
454   virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE {
455     return true;
456   }
457
458   virtual bool IsCommandIdVisible(int command_id) const OVERRIDE {
459     return command_id != IDC_MENU_INVISIBLE;
460   }
461
462   virtual bool GetAcceleratorForCommandId(
463       int command_id,
464       ui::Accelerator* accelerator) OVERRIDE {
465     return false;
466   }
467
468   virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE {
469   }
470
471  private:
472   scoped_ptr<ui::SimpleMenuModel> menu_model_;
473   scoped_ptr<views::MenuRunner> menu_runner_;
474
475   DISALLOW_COPY_AND_ASSIGN(SimpleMenuDelegate);
476 };
477
478 }  // namespace
479
480 TEST_F(AccessibilityEventRouterViewsTest, MenuIndexAndCountForInvisibleMenu) {
481   SimpleMenuDelegate menu_delegate;
482   views::MenuItemView* menu = menu_delegate.BuildMenu();
483   views::View* menu_container = menu->CreateSubmenu();
484
485   struct TestCase {
486     int command_id;
487     int expected_index;
488     int expected_count;
489   } kTestCases[] = {
490     { SimpleMenuDelegate::IDC_MENU_ITEM_1, 0, 3 },
491     { SimpleMenuDelegate::IDC_MENU_ITEM_2, 1, 3 },
492     { SimpleMenuDelegate::IDC_MENU_INVISIBLE, 0, 3 },
493     { SimpleMenuDelegate::IDC_MENU_ITEM_3, 2, 3 },
494   };
495
496   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
497     int index = 0;
498     int count = 0;
499
500     AccessibilityEventRouterViews::RecursiveGetMenuItemIndexAndCount(
501         menu_container,
502         menu->GetMenuItemByID(kTestCases[i].command_id),
503         &index,
504         &count);
505     EXPECT_EQ(kTestCases[i].expected_index, index) << "Case " << i;
506     EXPECT_EQ(kTestCases[i].expected_count, count) << "Case " << i;
507   }
508 }