1ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com// Use of this source code is governed by a BSD-style license that can be 3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com// found in the LICENSE file. 4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com 5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com#include <string> 6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com 7ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com#include "base/message_loop/message_loop.h" 8ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com#include "base/strings/string_util.h" 9ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com#include "base/strings/utf_string_conversions.h" 10ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com#include "chrome/browser/accessibility/accessibility_extension_api.h" 11ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com#include "chrome/browser/accessibility/accessibility_extension_api_constants.h" 127d330eb19cd3c9278abce68ca0e3efabf2ec8f87commit-bot@chromium.org#include "chrome/browser/ui/views/accessibility/accessibility_event_router_views.h" 13ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com#include "chrome/test/base/testing_profile.h" 14a8916ffd90c04dc6cc1fb9ba94af2ff950284fadcommit-bot@chromium.org#include "testing/gtest/include/gtest/gtest.h" 15c44be0e9e4cee5402909c06370a630eee188a8f3bsalomon#include "ui/accessibility/ax_enums.h" 16c44be0e9e4cee5402909c06370a630eee188a8f3bsalomon#include "ui/accessibility/ax_view_state.h" 17ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com#include "ui/base/models/simple_menu_model.h" 18ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com#include "ui/views/controls/button/label_button.h" 19ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com#include "ui/views/controls/label.h" 207d330eb19cd3c9278abce68ca0e3efabf2ec8f87commit-bot@chromium.org#include "ui/views/controls/menu/menu_item_view.h" 217d330eb19cd3c9278abce68ca0e3efabf2ec8f87commit-bot@chromium.org#include "ui/views/controls/menu/menu_model_adapter.h" 227d330eb19cd3c9278abce68ca0e3efabf2ec8f87commit-bot@chromium.org#include "ui/views/controls/menu/menu_runner.h" 237d330eb19cd3c9278abce68ca0e3efabf2ec8f87commit-bot@chromium.org#include "ui/views/controls/menu/submenu_view.h" 247d330eb19cd3c9278abce68ca0e3efabf2ec8f87commit-bot@chromium.org#include "ui/views/layout/grid_layout.h" 257d330eb19cd3c9278abce68ca0e3efabf2ec8f87commit-bot@chromium.org#include "ui/views/test/test_views_delegate.h" 261d86ee8363018d71245d23573619473ae7e7d1c9robertphillips#include "ui/views/widget/native_widget.h" 277d330eb19cd3c9278abce68ca0e3efabf2ec8f87commit-bot@chromium.org#include "ui/views/widget/root_view.h" 287d330eb19cd3c9278abce68ca0e3efabf2ec8f87commit-bot@chromium.org#include "ui/views/widget/widget.h" 297d330eb19cd3c9278abce68ca0e3efabf2ec8f87commit-bot@chromium.org#include "ui/views/widget/widget_delegate.h" 301d86ee8363018d71245d23573619473ae7e7d1c9robertphillips 317d330eb19cd3c9278abce68ca0e3efabf2ec8f87commit-bot@chromium.org#if defined(OS_WIN) 327d330eb19cd3c9278abce68ca0e3efabf2ec8f87commit-bot@chromium.org#include "ui/base/win/scoped_ole_initializer.h" 33ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com#endif 34c9b2c885be8d2bb39f1d75cc316278fa8d0fa9f0commit-bot@chromium.org 35ade9a3485e78d471f5f0902e9e50a2ec74c88e76skia.committer@gmail.com#if defined(USE_AURA) 3617dabfc38d5ecba73845fa11e3cb3a617f52ead0robertphillips#include "ui/aura/test/aura_test_helper.h" 3717dabfc38d5ecba73845fa11e3cb3a617f52ead0robertphillips#include "ui/aura/window_event_dispatcher.h" 3817dabfc38d5ecba73845fa11e3cb3a617f52ead0robertphillips#include "ui/compositor/test/context_factories_for_test.h" 3917dabfc38d5ecba73845fa11e3cb3a617f52ead0robertphillips#include "ui/wm/core/default_activation_client.h" 40ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com#endif 41ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com 42d537341e16524d1e22ac5e6c8b9c8f274ba1833crobertphillipsusing base::ASCIIToUTF16; 43ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com 44a8916ffd90c04dc6cc1fb9ba94af2ff950284fadcommit-bot@chromium.orgclass AccessibilityViewsDelegate : public views::TestViewsDelegate { 45a8916ffd90c04dc6cc1fb9ba94af2ff950284fadcommit-bot@chromium.org public: 46ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com AccessibilityViewsDelegate() {} 477801faaab9bf7dd0ac67e859c4e284e74f7bd46fcommit-bot@chromium.org virtual ~AccessibilityViewsDelegate() {} 487801faaab9bf7dd0ac67e859c4e284e74f7bd46fcommit-bot@chromium.org 49c9b2c885be8d2bb39f1d75cc316278fa8d0fa9f0commit-bot@chromium.org // Overridden from views::TestViewsDelegate: 50c9b2c885be8d2bb39f1d75cc316278fa8d0fa9f0commit-bot@chromium.org virtual void NotifyAccessibilityEvent( 51ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com views::View* view, ui::AXEvent event_type) OVERRIDE { 527d330eb19cd3c9278abce68ca0e3efabf2ec8f87commit-bot@chromium.org AccessibilityEventRouterViews::GetInstance()->HandleAccessibilityEvent( 537d330eb19cd3c9278abce68ca0e3efabf2ec8f87commit-bot@chromium.org view, event_type); 5417dabfc38d5ecba73845fa11e3cb3a617f52ead0robertphillips } 557801faaab9bf7dd0ac67e859c4e284e74f7bd46fcommit-bot@chromium.org 56ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com private: 5767ed64e9aa70f5a95a2d309f9b73dc0009f3ed8ccommit-bot@chromium.org DISALLOW_COPY_AND_ASSIGN(AccessibilityViewsDelegate); 58a8916ffd90c04dc6cc1fb9ba94af2ff950284fadcommit-bot@chromium.org}; 59a8916ffd90c04dc6cc1fb9ba94af2ff950284fadcommit-bot@chromium.org 6017dabfc38d5ecba73845fa11e3cb3a617f52ead0robertphillipsclass AccessibilityWindowDelegate : public views::WidgetDelegate { 617801faaab9bf7dd0ac67e859c4e284e74f7bd46fcommit-bot@chromium.org public: 62a8916ffd90c04dc6cc1fb9ba94af2ff950284fadcommit-bot@chromium.org explicit AccessibilityWindowDelegate(views::View* contents) 63a8916ffd90c04dc6cc1fb9ba94af2ff950284fadcommit-bot@chromium.org : contents_(contents) { } 641d86ee8363018d71245d23573619473ae7e7d1c9robertphillips 65d537341e16524d1e22ac5e6c8b9c8f274ba1833crobertphillips // Overridden from views::WidgetDelegate: 668b169311b59ab84e8ca6f3630a1e960cc1be751erobertphillips@google.com virtual void DeleteDelegate() OVERRIDE { delete this; } 677801faaab9bf7dd0ac67e859c4e284e74f7bd46fcommit-bot@chromium.org virtual views::View* GetContentsView() OVERRIDE { return contents_; } 687801faaab9bf7dd0ac67e859c4e284e74f7bd46fcommit-bot@chromium.org virtual const views::Widget* GetWidget() const OVERRIDE { 697801faaab9bf7dd0ac67e859c4e284e74f7bd46fcommit-bot@chromium.org return contents_->GetWidget(); 70ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com } 711d86ee8363018d71245d23573619473ae7e7d1c9robertphillips virtual views::Widget* GetWidget() OVERRIDE { return contents_->GetWidget(); } 72ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com 73ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com private: 74c9b2c885be8d2bb39f1d75cc316278fa8d0fa9f0commit-bot@chromium.org views::View* contents_; 75c9b2c885be8d2bb39f1d75cc316278fa8d0fa9f0commit-bot@chromium.org 761d86ee8363018d71245d23573619473ae7e7d1c9robertphillips DISALLOW_COPY_AND_ASSIGN(AccessibilityWindowDelegate); 77ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com}; 781d86ee8363018d71245d23573619473ae7e7d1c9robertphillips 791d86ee8363018d71245d23573619473ae7e7d1c9robertphillipsclass ViewWithNameAndRole : public views::View { 801d86ee8363018d71245d23573619473ae7e7d1c9robertphillips public: 811d86ee8363018d71245d23573619473ae7e7d1c9robertphillips explicit ViewWithNameAndRole(const base::string16& name, 821d86ee8363018d71245d23573619473ae7e7d1c9robertphillips ui::AXRole role) 831d86ee8363018d71245d23573619473ae7e7d1c9robertphillips : name_(name), 84320c92380fe6a43dffbcd3f9e7c99897da44298drobertphillips role_(role) { 85320c92380fe6a43dffbcd3f9e7c99897da44298drobertphillips } 86320c92380fe6a43dffbcd3f9e7c99897da44298drobertphillips 87320c92380fe6a43dffbcd3f9e7c99897da44298drobertphillips virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE { 88320c92380fe6a43dffbcd3f9e7c99897da44298drobertphillips views::View::GetAccessibleState(state); 89320c92380fe6a43dffbcd3f9e7c99897da44298drobertphillips state->name = name_; 901d86ee8363018d71245d23573619473ae7e7d1c9robertphillips state->role = role_; 911d86ee8363018d71245d23573619473ae7e7d1c9robertphillips } 921d86ee8363018d71245d23573619473ae7e7d1c9robertphillips 931d86ee8363018d71245d23573619473ae7e7d1c9robertphillips void set_name(const base::string16& name) { name_ = name; } 941d86ee8363018d71245d23573619473ae7e7d1c9robertphillips 951d86ee8363018d71245d23573619473ae7e7d1c9robertphillips private: 96952841bf41a81228c23d16c7204b458abe0d7136robertphillips base::string16 name_; 97952841bf41a81228c23d16c7204b458abe0d7136robertphillips ui::AXRole role_; 981d86ee8363018d71245d23573619473ae7e7d1c9robertphillips DISALLOW_COPY_AND_ASSIGN(ViewWithNameAndRole); 991d86ee8363018d71245d23573619473ae7e7d1c9robertphillips}; 100ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com 101952841bf41a81228c23d16c7204b458abe0d7136robertphillipsclass AccessibilityEventRouterViewsTest 102952841bf41a81228c23d16c7204b458abe0d7136robertphillips : public testing::Test { 103952841bf41a81228c23d16c7204b458abe0d7136robertphillips public: 104952841bf41a81228c23d16c7204b458abe0d7136robertphillips AccessibilityEventRouterViewsTest() : control_event_count_(0) { 105952841bf41a81228c23d16c7204b458abe0d7136robertphillips } 106952841bf41a81228c23d16c7204b458abe0d7136robertphillips 10750df4d013f840749f70d1759c23c4217e727fd54skia.committer@gmail.com virtual void SetUp() { 108c9b2c885be8d2bb39f1d75cc316278fa8d0fa9f0commit-bot@chromium.org#if defined(OS_WIN) 109c4f30b1074e1068d6fc0f368428f410ed6d4b2b5robertphillips ole_initializer_.reset(new ui::ScopedOleInitializer()); 11050df4d013f840749f70d1759c23c4217e727fd54skia.committer@gmail.com#endif 111c9b2c885be8d2bb39f1d75cc316278fa8d0fa9f0commit-bot@chromium.org views::ViewsDelegate::views_delegate = new AccessibilityViewsDelegate(); 112c9b2c885be8d2bb39f1d75cc316278fa8d0fa9f0commit-bot@chromium.org#if defined(USE_AURA) 113c9b2c885be8d2bb39f1d75cc316278fa8d0fa9f0commit-bot@chromium.org // The ContextFactory must exist before any Compositors are created. 11450df4d013f840749f70d1759c23c4217e727fd54skia.committer@gmail.com bool enable_pixel_output = false; 1153fddf0eed6dc2873bcc8e584f435c6cd34964518commit-bot@chromium.org ui::ContextFactory* context_factory = 1163fddf0eed6dc2873bcc8e584f435c6cd34964518commit-bot@chromium.org ui::InitializeContextFactoryForTests(enable_pixel_output); 117759c16e20dc42577226c8805bfea92d8bacb14d8reed@google.com 118ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com aura_test_helper_.reset(new aura::test::AuraTestHelper(&message_loop_)); 1197801faaab9bf7dd0ac67e859c4e284e74f7bd46fcommit-bot@chromium.org aura_test_helper_->SetUp(context_factory); 1207801faaab9bf7dd0ac67e859c4e284e74f7bd46fcommit-bot@chromium.org new wm::DefaultActivationClient(aura_test_helper_->root_window()); 121320c92380fe6a43dffbcd3f9e7c99897da44298drobertphillips#endif // USE_AURA 122320c92380fe6a43dffbcd3f9e7c99897da44298drobertphillips EnableAccessibilityAndListenToFocusNotifications(); 123320c92380fe6a43dffbcd3f9e7c99897da44298drobertphillips } 124320c92380fe6a43dffbcd3f9e7c99897da44298drobertphillips 125320c92380fe6a43dffbcd3f9e7c99897da44298drobertphillips virtual void TearDown() { 126320c92380fe6a43dffbcd3f9e7c99897da44298drobertphillips ClearCallback(); 127320c92380fe6a43dffbcd3f9e7c99897da44298drobertphillips#if defined(USE_AURA) 128320c92380fe6a43dffbcd3f9e7c99897da44298drobertphillips aura_test_helper_->TearDown(); 129320c92380fe6a43dffbcd3f9e7c99897da44298drobertphillips ui::TerminateContextFactoryForTests(); 130320c92380fe6a43dffbcd3f9e7c99897da44298drobertphillips#endif 131320c92380fe6a43dffbcd3f9e7c99897da44298drobertphillips delete views::ViewsDelegate::views_delegate; 132320c92380fe6a43dffbcd3f9e7c99897da44298drobertphillips 133ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com // The Widget's FocusManager is deleted using DeleteSoon - this 1341d86ee8363018d71245d23573619473ae7e7d1c9robertphillips // forces it to be deleted now, so we don't have any memory leaks 13550df4d013f840749f70d1759c23c4217e727fd54skia.committer@gmail.com // when this method exits. 136952841bf41a81228c23d16c7204b458abe0d7136robertphillips base::MessageLoop::current()->RunUntilIdle(); 137952841bf41a81228c23d16c7204b458abe0d7136robertphillips 138952841bf41a81228c23d16c7204b458abe0d7136robertphillips#if defined(OS_WIN) 139952841bf41a81228c23d16c7204b458abe0d7136robertphillips ole_initializer_.reset(); 140952841bf41a81228c23d16c7204b458abe0d7136robertphillips#endif 141952841bf41a81228c23d16c7204b458abe0d7136robertphillips } 142952841bf41a81228c23d16c7204b458abe0d7136robertphillips 143952841bf41a81228c23d16c7204b458abe0d7136robertphillips views::Widget* CreateWindowWithContents(views::View* contents) { 14450df4d013f840749f70d1759c23c4217e727fd54skia.committer@gmail.com gfx::NativeWindow context = NULL; 1457d330eb19cd3c9278abce68ca0e3efabf2ec8f87commit-bot@chromium.org#if defined(USE_AURA) 146c9b2c885be8d2bb39f1d75cc316278fa8d0fa9f0commit-bot@chromium.org context = aura_test_helper_->root_window(); 1471d86ee8363018d71245d23573619473ae7e7d1c9robertphillips#endif 148c9b2c885be8d2bb39f1d75cc316278fa8d0fa9f0commit-bot@chromium.org views::Widget* widget = views::Widget::CreateWindowWithContextAndBounds( 1497d330eb19cd3c9278abce68ca0e3efabf2ec8f87commit-bot@chromium.org new AccessibilityWindowDelegate(contents), 1507d330eb19cd3c9278abce68ca0e3efabf2ec8f87commit-bot@chromium.org context, 151ac10a2d039c5d52eed66e27cbbc503ab523c1cd5reed@google.com 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 199TEST_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 256TEST_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 286TEST_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 318TEST_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 355TEST_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 390TEST_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 427TEST_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 453namespace { 454 455class 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 virtual ~SimpleMenuDelegate() {} 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 virtual bool IsCommandIdChecked(int command_id) const OVERRIDE { 483 return false; 484 } 485 486 virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE { 487 return true; 488 } 489 490 virtual bool IsCommandIdVisible(int command_id) const OVERRIDE { 491 return command_id != IDC_MENU_INVISIBLE; 492 } 493 494 virtual bool GetAcceleratorForCommandId( 495 int command_id, 496 ui::Accelerator* accelerator) OVERRIDE { 497 return false; 498 } 499 500 virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE { 501 } 502 503 private: 504 scoped_ptr<ui::SimpleMenuModel> menu_model_; 505 scoped_ptr<views::MenuModelAdapter> menu_adapter_; 506 scoped_ptr<views::MenuRunner> menu_runner_; 507 508 DISALLOW_COPY_AND_ASSIGN(SimpleMenuDelegate); 509}; 510 511} // namespace 512 513TEST_F(AccessibilityEventRouterViewsTest, MenuIndexAndCountForInvisibleMenu) { 514 SimpleMenuDelegate menu_delegate; 515 views::MenuItemView* menu = menu_delegate.BuildMenu(); 516 views::View* menu_container = menu->CreateSubmenu(); 517 518 struct TestCase { 519 int command_id; 520 int expected_index; 521 int expected_count; 522 } kTestCases[] = { 523 { SimpleMenuDelegate::IDC_MENU_ITEM_1, 0, 3 }, 524 { SimpleMenuDelegate::IDC_MENU_ITEM_2, 1, 3 }, 525 { SimpleMenuDelegate::IDC_MENU_INVISIBLE, 0, 3 }, 526 { SimpleMenuDelegate::IDC_MENU_ITEM_3, 2, 3 }, 527 }; 528 529 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) { 530 int index = 0; 531 int count = 0; 532 533 AccessibilityEventRouterViews::RecursiveGetMenuItemIndexAndCount( 534 menu_container, 535 menu->GetMenuItemByID(kTestCases[i].command_id), 536 &index, 537 &count); 538 EXPECT_EQ(kTestCases[i].expected_index, index) << "Case " << i; 539 EXPECT_EQ(kTestCases[i].expected_count, count) << "Case " << i; 540 } 541} 542