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