Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / views / accessibility / accessibility_event_router_views_unittest.cc
blob4b86aebdc846314899b5a2381fec7bb354b47619
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.
5 #include <string>
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/base/accessibility/accessibility_types.h"
16 #include "ui/base/accessibility/accessible_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"
30 #if defined(OS_WIN)
31 #include "ui/base/win/scoped_ole_initializer.h"
32 #endif
34 #if defined(USE_AURA)
35 #include "ui/aura/root_window.h"
36 #include "ui/aura/test/aura_test_helper.h"
37 #endif
39 using base::ASCIIToUTF16;
41 class AccessibilityViewsDelegate : public views::TestViewsDelegate {
42 public:
43 AccessibilityViewsDelegate() {}
44 virtual ~AccessibilityViewsDelegate() {}
46 // Overridden from views::TestViewsDelegate:
47 virtual void NotifyAccessibilityEvent(
48 views::View* view, ui::AccessibilityTypes::Event event_type) OVERRIDE {
49 AccessibilityEventRouterViews::GetInstance()->HandleAccessibilityEvent(
50 view, event_type);
53 DISALLOW_COPY_AND_ASSIGN(AccessibilityViewsDelegate);
56 class AccessibilityWindowDelegate : public views::WidgetDelegate {
57 public:
58 explicit AccessibilityWindowDelegate(views::View* contents)
59 : contents_(contents) { }
61 // Overridden from views::WidgetDelegate:
62 virtual void DeleteDelegate() OVERRIDE { delete this; }
63 virtual views::View* GetContentsView() OVERRIDE { return contents_; }
64 virtual const views::Widget* GetWidget() const OVERRIDE {
65 return contents_->GetWidget();
67 virtual views::Widget* GetWidget() OVERRIDE { return contents_->GetWidget(); }
69 private:
70 views::View* contents_;
72 DISALLOW_COPY_AND_ASSIGN(AccessibilityWindowDelegate);
75 class ViewWithNameAndRole : public views::View {
76 public:
77 explicit ViewWithNameAndRole(const base::string16& name,
78 ui::AccessibilityTypes::Role role)
79 : name_(name),
80 role_(role) {
83 virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE {
84 views::View::GetAccessibleState(state);
85 state->name = name_;
86 state->role = role_;
89 void set_name(const base::string16& name) { name_ = name; }
91 private:
92 base::string16 name_;
93 ui::AccessibilityTypes::Role role_;
94 DISALLOW_COPY_AND_ASSIGN(ViewWithNameAndRole);
97 class AccessibilityEventRouterViewsTest
98 : public testing::Test {
99 public:
100 AccessibilityEventRouterViewsTest() : control_event_count_(0) {
103 virtual void SetUp() {
104 #if defined(OS_WIN)
105 ole_initializer_.reset(new ui::ScopedOleInitializer());
106 #endif
107 views::ViewsDelegate::views_delegate = new AccessibilityViewsDelegate();
108 #if defined(USE_AURA)
109 aura_test_helper_.reset(new aura::test::AuraTestHelper(&message_loop_));
110 bool allow_test_contexts = true;
111 aura_test_helper_->SetUp(allow_test_contexts);
112 #endif // USE_AURA
113 EnableAccessibilityAndListenToFocusNotifications();
116 virtual void TearDown() {
117 ClearCallback();
118 #if defined(USE_AURA)
119 aura_test_helper_->TearDown();
120 #endif
121 delete views::ViewsDelegate::views_delegate;
122 views::ViewsDelegate::views_delegate = NULL;
124 // The Widget's FocusManager is deleted using DeleteSoon - this
125 // forces it to be deleted now, so we don't have any memory leaks
126 // when this method exits.
127 base::MessageLoop::current()->RunUntilIdle();
129 #if defined(OS_WIN)
130 ole_initializer_.reset();
131 #endif
134 views::Widget* CreateWindowWithContents(views::View* contents) {
135 gfx::NativeView context = NULL;
136 #if defined(USE_AURA)
137 context = aura_test_helper_->root_window();
138 #endif
139 views::Widget* widget = views::Widget::CreateWindowWithContextAndBounds(
140 new AccessibilityWindowDelegate(contents),
141 context,
142 gfx::Rect(0, 0, 500, 500));
144 // Create a profile and associate it with this window.
145 widget->SetNativeWindowProperty(Profile::kProfileKey, &profile_);
147 return widget;
150 void EnableAccessibilityAndListenToFocusNotifications() {
151 // Switch on accessibility event notifications.
152 ExtensionAccessibilityEventRouter* accessibility_event_router =
153 ExtensionAccessibilityEventRouter::GetInstance();
154 accessibility_event_router->SetAccessibilityEnabled(true);
155 accessibility_event_router->SetControlEventCallbackForTesting(base::Bind(
156 &AccessibilityEventRouterViewsTest::OnControlEvent,
157 base::Unretained(this)));
160 void ClearCallback() {
161 ExtensionAccessibilityEventRouter* accessibility_event_router =
162 ExtensionAccessibilityEventRouter::GetInstance();
163 accessibility_event_router->ClearControlEventCallback();
166 protected:
167 // Handle Focus event.
168 virtual void OnControlEvent(ui::AccessibilityTypes::Event event,
169 const AccessibilityControlInfo* info) {
170 control_event_count_++;
171 last_control_type_ = info->type();
172 last_control_name_ = info->name();
173 last_control_context_ = info->context();
176 base::MessageLoopForUI message_loop_;
177 int control_event_count_;
178 std::string last_control_type_;
179 std::string last_control_name_;
180 std::string last_control_context_;
181 TestingProfile profile_;
182 #if defined(OS_WIN)
183 scoped_ptr<ui::ScopedOleInitializer> ole_initializer_;
184 #endif
185 #if defined(USE_AURA)
186 scoped_ptr<aura::test::AuraTestHelper> aura_test_helper_;
187 #endif
190 TEST_F(AccessibilityEventRouterViewsTest, TestFocusNotification) {
191 const char kButton1ASCII[] = "Button1";
192 const char kButton2ASCII[] = "Button2";
193 const char kButton3ASCII[] = "Button3";
194 const char kButton3NewASCII[] = "Button3New";
196 // Create a contents view with 3 buttons.
197 views::View* contents = new views::View();
198 views::LabelButton* button1 = new views::LabelButton(
199 NULL, ASCIIToUTF16(kButton1ASCII));
200 button1->SetStyle(views::Button::STYLE_BUTTON);
201 contents->AddChildView(button1);
202 views::LabelButton* button2 = new views::LabelButton(
203 NULL, ASCIIToUTF16(kButton2ASCII));
204 button2->SetStyle(views::Button::STYLE_BUTTON);
205 contents->AddChildView(button2);
206 views::LabelButton* button3 = new views::LabelButton(
207 NULL, ASCIIToUTF16(kButton3ASCII));
208 button3->SetStyle(views::Button::STYLE_BUTTON);
209 contents->AddChildView(button3);
211 // Put the view in a window.
212 views::Widget* window = CreateWindowWithContents(contents);
213 window->Show();
215 // Set focus to the first button initially and run message loop to execute
216 // callback.
217 button1->RequestFocus();
218 base::MessageLoop::current()->RunUntilIdle();
220 // Change the accessible name of button3.
221 button3->SetAccessibleName(ASCIIToUTF16(kButton3NewASCII));
223 // Advance focus to the next button and test that we got the
224 // expected notification with the name of button 2.
225 views::FocusManager* focus_manager = contents->GetWidget()->GetFocusManager();
226 control_event_count_ = 0;
227 focus_manager->AdvanceFocus(false);
228 base::MessageLoop::current()->RunUntilIdle();
229 EXPECT_EQ(1, control_event_count_);
230 EXPECT_EQ(kButton2ASCII, last_control_name_);
232 // Advance to button 3. Expect the new accessible name we assigned.
233 focus_manager->AdvanceFocus(false);
234 base::MessageLoop::current()->RunUntilIdle();
235 EXPECT_EQ(2, control_event_count_);
236 EXPECT_EQ(kButton3NewASCII, last_control_name_);
238 // Advance to button 1 and check the notification.
239 focus_manager->AdvanceFocus(false);
240 base::MessageLoop::current()->RunUntilIdle();
241 EXPECT_EQ(3, control_event_count_);
242 EXPECT_EQ(kButton1ASCII, last_control_name_);
244 window->CloseNow();
247 TEST_F(AccessibilityEventRouterViewsTest, TestToolbarContext) {
248 const char kToolbarNameASCII[] = "MyToolbar";
249 const char kButtonNameASCII[] = "MyButton";
251 // Create a toolbar with a button.
252 views::View* contents = new ViewWithNameAndRole(
253 ASCIIToUTF16(kToolbarNameASCII),
254 ui::AccessibilityTypes::ROLE_TOOLBAR);
255 views::LabelButton* button = new views::LabelButton(
256 NULL, ASCIIToUTF16(kButtonNameASCII));
257 button->SetStyle(views::Button::STYLE_BUTTON);
258 contents->AddChildView(button);
260 // Put the view in a window.
261 views::Widget* window = CreateWindowWithContents(contents);
263 // Set focus to the button.
264 control_event_count_ = 0;
265 button->RequestFocus();
267 base::MessageLoop::current()->RunUntilIdle();
269 // Test that we got the event with the expected name and context.
270 EXPECT_EQ(1, control_event_count_);
271 EXPECT_EQ(kButtonNameASCII, last_control_name_);
272 EXPECT_EQ(kToolbarNameASCII, last_control_context_);
274 window->CloseNow();
277 TEST_F(AccessibilityEventRouterViewsTest, TestAlertContext) {
278 const char kAlertTextASCII[] = "MyAlertText";
279 const char kButtonNameASCII[] = "MyButton";
281 // Create an alert with static text and a button, similar to an infobar.
282 views::View* contents = new ViewWithNameAndRole(
283 base::string16(),
284 ui::AccessibilityTypes::ROLE_ALERT);
285 views::Label* label = new views::Label(ASCIIToUTF16(kAlertTextASCII));
286 contents->AddChildView(label);
287 views::LabelButton* button = new views::LabelButton(
288 NULL, ASCIIToUTF16(kButtonNameASCII));
289 button->SetStyle(views::Button::STYLE_BUTTON);
290 contents->AddChildView(button);
292 // Put the view in a window.
293 views::Widget* window = CreateWindowWithContents(contents);
295 // Set focus to the button.
296 control_event_count_ = 0;
297 button->RequestFocus();
299 base::MessageLoop::current()->RunUntilIdle();
301 // Test that we got the event with the expected name and context.
302 EXPECT_EQ(1, control_event_count_);
303 EXPECT_EQ(kButtonNameASCII, last_control_name_);
304 EXPECT_EQ(kAlertTextASCII, last_control_context_);
306 window->CloseNow();
309 TEST_F(AccessibilityEventRouterViewsTest, StateChangeAfterNotification) {
310 const char kContentsNameASCII[] = "Contents";
311 const char kOldNameASCII[] = "OldName";
312 const char kNewNameASCII[] = "NewName";
314 // Create a toolbar with a button.
315 views::View* contents = new ViewWithNameAndRole(
316 ASCIIToUTF16(kContentsNameASCII),
317 ui::AccessibilityTypes::ROLE_CLIENT);
318 ViewWithNameAndRole* child = new ViewWithNameAndRole(
319 ASCIIToUTF16(kOldNameASCII),
320 ui::AccessibilityTypes::ROLE_PUSHBUTTON);
321 child->SetFocusable(true);
322 contents->AddChildView(child);
324 // Put the view in a window.
325 views::Widget* window = CreateWindowWithContents(contents);
327 // Set focus to the child view.
328 control_event_count_ = 0;
329 child->RequestFocus();
331 // Change the child's name after the focus notification.
332 child->set_name(ASCIIToUTF16(kNewNameASCII));
334 // We shouldn't get the notification right away.
335 EXPECT_EQ(0, control_event_count_);
337 // Process anything in the event loop. Now we should get the notification,
338 // and it should give us the new control name, not the old one.
339 base::MessageLoop::current()->RunUntilIdle();
340 EXPECT_EQ(1, control_event_count_);
341 EXPECT_EQ(kNewNameASCII, last_control_name_);
343 window->CloseNow();
346 TEST_F(AccessibilityEventRouterViewsTest, NotificationOnDeletedObject) {
347 const char kContentsNameASCII[] = "Contents";
348 const char kNameASCII[] = "OldName";
350 // Create a toolbar with a button.
351 views::View* contents = new ViewWithNameAndRole(
352 ASCIIToUTF16(kContentsNameASCII),
353 ui::AccessibilityTypes::ROLE_CLIENT);
354 ViewWithNameAndRole* child = new ViewWithNameAndRole(
355 ASCIIToUTF16(kNameASCII),
356 ui::AccessibilityTypes::ROLE_PUSHBUTTON);
357 child->SetFocusable(true);
358 contents->AddChildView(child);
360 // Put the view in a window.
361 views::Widget* window = CreateWindowWithContents(contents);
363 // Set focus to the child view.
364 control_event_count_ = 0;
365 child->RequestFocus();
367 // Delete the child!
368 delete child;
370 // We shouldn't get the notification right away.
371 EXPECT_EQ(0, control_event_count_);
373 // Process anything in the event loop. We shouldn't get a notification
374 // because the view is no longer valid, and this shouldn't crash.
375 base::MessageLoop::current()->RunUntilIdle();
376 EXPECT_EQ(0, control_event_count_);
378 window->CloseNow();
381 TEST_F(AccessibilityEventRouterViewsTest, AlertsFromWindowAndControl) {
382 const char kButtonASCII[] = "Button";
383 const char* kTypeAlert = extension_accessibility_api_constants::kTypeAlert;
384 const char* kTypeWindow = extension_accessibility_api_constants::kTypeWindow;
386 // Create a contents view with a button.
387 views::View* contents = new views::View();
388 views::LabelButton* button = new views::LabelButton(
389 NULL, ASCIIToUTF16(kButtonASCII));
390 button->SetStyle(views::Button::STYLE_BUTTON);
391 contents->AddChildView(button);
393 // Put the view in a window.
394 views::Widget* window = CreateWindowWithContents(contents);
395 window->Show();
397 // Send an alert event from the button and let the event loop run.
398 control_event_count_ = 0;
399 button->NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_ALERT, true);
400 base::MessageLoop::current()->RunUntilIdle();
402 EXPECT_EQ(kTypeAlert, last_control_type_);
403 EXPECT_EQ(1, control_event_count_);
404 EXPECT_EQ(kButtonASCII, last_control_name_);
406 // Send an alert event from the window and let the event loop run.
407 control_event_count_ = 0;
408 window->GetRootView()->NotifyAccessibilityEvent(
409 ui::AccessibilityTypes::EVENT_ALERT, true);
410 base::MessageLoop::current()->RunUntilIdle();
412 EXPECT_EQ(1, control_event_count_);
413 EXPECT_EQ(kTypeWindow, last_control_type_);
415 window->CloseNow();
418 namespace {
420 class SimpleMenuDelegate : public ui::SimpleMenuModel::Delegate {
421 public:
422 enum {
423 IDC_MENU_ITEM_1,
424 IDC_MENU_ITEM_2,
425 IDC_MENU_INVISIBLE,
426 IDC_MENU_ITEM_3,
429 SimpleMenuDelegate() {}
430 virtual ~SimpleMenuDelegate() {}
432 views::MenuItemView* BuildMenu() {
433 menu_model_.reset(new ui::SimpleMenuModel(this));
434 menu_model_->AddItem(IDC_MENU_ITEM_1, ASCIIToUTF16("Item 1"));
435 menu_model_->AddItem(IDC_MENU_ITEM_2, ASCIIToUTF16("Item 2"));
436 menu_model_->AddSeparator(ui::NORMAL_SEPARATOR);
437 menu_model_->AddItem(IDC_MENU_INVISIBLE, ASCIIToUTF16("Invisible"));
438 menu_model_->AddSeparator(ui::NORMAL_SEPARATOR);
439 menu_model_->AddItem(IDC_MENU_ITEM_3, ASCIIToUTF16("Item 3"));
441 menu_runner_.reset(new views::MenuRunner(menu_model_.get()));
442 return menu_runner_->GetMenu();
445 virtual bool IsCommandIdChecked(int command_id) const OVERRIDE {
446 return false;
449 virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE {
450 return true;
453 virtual bool IsCommandIdVisible(int command_id) const OVERRIDE {
454 return command_id != IDC_MENU_INVISIBLE;
457 virtual bool GetAcceleratorForCommandId(
458 int command_id,
459 ui::Accelerator* accelerator) OVERRIDE {
460 return false;
463 virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE {
466 private:
467 scoped_ptr<ui::SimpleMenuModel> menu_model_;
468 scoped_ptr<views::MenuRunner> menu_runner_;
470 DISALLOW_COPY_AND_ASSIGN(SimpleMenuDelegate);
473 } // namespace
475 TEST_F(AccessibilityEventRouterViewsTest, MenuIndexAndCountForInvisibleMenu) {
476 SimpleMenuDelegate menu_delegate;
477 views::MenuItemView* menu = menu_delegate.BuildMenu();
478 views::View* menu_container = menu->CreateSubmenu();
480 struct TestCase {
481 int command_id;
482 int expected_index;
483 int expected_count;
484 } kTestCases[] = {
485 { SimpleMenuDelegate::IDC_MENU_ITEM_1, 0, 3 },
486 { SimpleMenuDelegate::IDC_MENU_ITEM_2, 1, 3 },
487 { SimpleMenuDelegate::IDC_MENU_INVISIBLE, 0, 3 },
488 { SimpleMenuDelegate::IDC_MENU_ITEM_3, 2, 3 },
491 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
492 int index = 0;
493 int count = 0;
495 AccessibilityEventRouterViews::RecursiveGetMenuItemIndexAndCount(
496 menu_container,
497 menu->GetMenuItemByID(kTestCases[i].command_id),
498 &index,
499 &count);
500 EXPECT_EQ(kTestCases[i].expected_index, index) << "Case " << i;
501 EXPECT_EQ(kTestCases[i].expected_count, count) << "Case " << i;