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