Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/views/controls/menu/menu_controller.h" | 5 #include "ui/views/controls/menu/menu_controller.h" |
| 6 | 6 |
| 7 #include "base/run_loop.h" | |
| 8 #include "base/strings/utf_string_conversions.h" | 7 #include "base/strings/utf_string_conversions.h" |
| 9 #include "ui/aura/scoped_window_targeter.h" | 8 #include "ui/aura/scoped_window_targeter.h" |
| 10 #include "ui/aura/window.h" | 9 #include "ui/aura/window.h" |
| 11 #include "ui/events/event_handler.h" | 10 #include "ui/events/event_handler.h" |
| 12 #include "ui/events/null_event_targeter.h" | 11 #include "ui/events/null_event_targeter.h" |
| 13 #include "ui/events/platform/platform_event_source.h" | 12 #include "ui/events/test/event_generator.h" |
| 14 #include "ui/views/controls/menu/menu_item_view.h" | 13 #include "ui/views/controls/menu/menu_item_view.h" |
| 15 #include "ui/views/controls/menu/submenu_view.h" | 14 #include "ui/views/controls/menu/submenu_view.h" |
| 16 #include "ui/views/test/views_test_base.h" | 15 #include "ui/views/test/views_test_base.h" |
| 17 #include "ui/wm/public/dispatcher_client.h" | |
| 18 | 16 |
| 19 #if defined(OS_WIN) | 17 #if defined(USE_X11) |
| 20 #include "base/message_loop/message_pump_dispatcher.h" | |
| 21 #elif defined(USE_X11) | |
| 22 #include <X11/Xlib.h> | 18 #include <X11/Xlib.h> |
| 23 #undef Bool | 19 #undef Bool |
| 24 #undef None | 20 #undef None |
| 25 #include "ui/events/devices/x11/device_data_manager_x11.h" | 21 #include "ui/events/devices/x11/device_data_manager_x11.h" |
| 26 #include "ui/events/test/events_test_utils_x11.h" | 22 #include "ui/events/test/events_test_utils_x11.h" |
| 27 #elif defined(USE_OZONE) | |
| 28 #include "ui/events/event.h" | |
| 29 #endif | 23 #endif |
| 30 | 24 |
| 31 namespace views { | 25 namespace views { |
| 32 | 26 |
| 33 namespace { | 27 namespace { |
| 34 | 28 |
| 35 class TestMenuItemView : public MenuItemView { | 29 class TestMenuItemView : public MenuItemView { |
| 36 public: | 30 public: |
| 37 TestMenuItemView() : MenuItemView(nullptr) {} | 31 TestMenuItemView() : MenuItemView(nullptr) {} |
| 38 ~TestMenuItemView() override {} | 32 ~TestMenuItemView() override {} |
| 39 | 33 |
| 40 private: | 34 private: |
| 41 DISALLOW_COPY_AND_ASSIGN(TestMenuItemView); | 35 DISALLOW_COPY_AND_ASSIGN(TestMenuItemView); |
| 42 }; | 36 }; |
| 43 | 37 |
| 44 class TestPlatformEventSource : public ui::PlatformEventSource { | |
| 45 public: | |
| 46 TestPlatformEventSource() { | |
| 47 #if defined(USE_X11) | |
| 48 ui::DeviceDataManagerX11::CreateInstance(); | |
| 49 #endif | |
| 50 } | |
| 51 ~TestPlatformEventSource() override {} | |
| 52 | |
| 53 uint32_t Dispatch(const ui::PlatformEvent& event) { | |
| 54 return DispatchEvent(event); | |
| 55 } | |
| 56 | |
| 57 private: | |
| 58 DISALLOW_COPY_AND_ASSIGN(TestPlatformEventSource); | |
| 59 }; | |
| 60 | |
| 61 class TestDispatcherClient : public aura::client::DispatcherClient { | |
| 62 public: | |
| 63 TestDispatcherClient() : dispatcher_(nullptr) {} | |
| 64 ~TestDispatcherClient() override {} | |
| 65 | |
| 66 base::MessagePumpDispatcher* dispatcher() { | |
| 67 return dispatcher_; | |
| 68 } | |
| 69 | |
| 70 // aura::client::DispatcherClient: | |
| 71 void PrepareNestedLoopClosures(base::MessagePumpDispatcher* dispatcher, | |
| 72 base::Closure* run_closure, | |
| 73 base::Closure* quit_closure) override { | |
| 74 scoped_ptr<base::RunLoop> run_loop(new base::RunLoop()); | |
| 75 *quit_closure = run_loop->QuitClosure(); | |
| 76 *run_closure = base::Bind(&TestDispatcherClient::RunNestedDispatcher, | |
| 77 base::Unretained(this), | |
| 78 base::Passed(&run_loop), | |
| 79 dispatcher); | |
| 80 } | |
| 81 | |
| 82 private: | |
| 83 void RunNestedDispatcher(scoped_ptr<base::RunLoop> run_loop, | |
| 84 base::MessagePumpDispatcher* dispatcher) { | |
| 85 base::AutoReset<base::MessagePumpDispatcher*> reset_dispatcher(&dispatcher_, | |
| 86 dispatcher); | |
| 87 base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); | |
| 88 base::MessageLoop::ScopedNestableTaskAllower allow(loop); | |
| 89 run_loop->Run(); | |
| 90 } | |
| 91 | |
| 92 base::MessagePumpDispatcher* dispatcher_; | |
| 93 | |
| 94 DISALLOW_COPY_AND_ASSIGN(TestDispatcherClient); | |
| 95 }; | |
| 96 | |
| 97 } // namespace | |
| 98 | |
| 99 class MenuControllerTest : public ViewsTestBase { | |
| 100 public: | |
| 101 MenuControllerTest() : controller_(nullptr) {} | |
| 102 ~MenuControllerTest() override { ResetMenuController(); } | |
| 103 | |
| 104 // Dispatches |count| number of items, each in a separate iteration of the | |
| 105 // message-loop, by posting a task. | |
| 106 void Step3_DispatchEvents(int count) { | |
| 107 base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); | |
| 108 base::MessageLoop::ScopedNestableTaskAllower allow(loop); | |
| 109 controller_->exit_type_ = MenuController::EXIT_ALL; | |
| 110 | |
| 111 DispatchEvent(); | |
| 112 if (count) { | |
| 113 base::MessageLoop::current()->PostTask( | |
| 114 FROM_HERE, | |
| 115 base::Bind(&MenuControllerTest::Step3_DispatchEvents, | |
| 116 base::Unretained(this), | |
| 117 count - 1)); | |
| 118 } else { | |
| 119 EXPECT_TRUE(run_loop_->running()); | |
| 120 run_loop_->Quit(); | |
| 121 } | |
| 122 } | |
| 123 | |
| 124 // Runs a nested message-loop that does not involve the menu itself. | |
| 125 void Step2_RunNestedLoop() { | |
| 126 base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); | |
| 127 base::MessageLoop::ScopedNestableTaskAllower allow(loop); | |
| 128 base::MessageLoop::current()->PostTask( | |
| 129 FROM_HERE, | |
| 130 base::Bind(&MenuControllerTest::Step3_DispatchEvents, | |
| 131 base::Unretained(this), | |
| 132 3)); | |
| 133 run_loop_.reset(new base::RunLoop()); | |
| 134 run_loop_->Run(); | |
| 135 } | |
| 136 | |
| 137 void Step1_RunMenu() { | |
| 138 base::MessageLoop::current()->PostTask( | |
| 139 FROM_HERE, | |
| 140 base::Bind(&MenuControllerTest::Step2_RunNestedLoop, | |
| 141 base::Unretained(this))); | |
| 142 scoped_ptr<Widget> owner(CreateOwnerWidget()); | |
| 143 RunMenu(owner.get()); | |
| 144 } | |
| 145 | |
| 146 scoped_ptr<Widget> CreateOwnerWidget() { | |
| 147 scoped_ptr<Widget> widget(new Widget); | |
| 148 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); | |
| 149 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
| 150 widget->Init(params); | |
| 151 widget->Show(); | |
| 152 | |
| 153 aura::client::SetDispatcherClient( | |
| 154 widget->GetNativeWindow()->GetRootWindow(), &dispatcher_client_); | |
| 155 return widget.Pass(); | |
| 156 } | |
| 157 | |
| 158 const MenuItemView* pending_state_item() const { | |
| 159 return controller_->pending_state_.item; | |
| 160 } | |
| 161 | |
| 162 void SetPendingStateItem(MenuItemView* item) { | |
| 163 controller_->pending_state_.item = item; | |
| 164 } | |
| 165 | |
| 166 void ResetSelection() { | |
| 167 controller_->SetSelection(nullptr, | |
| 168 MenuController::SELECTION_EXIT | | |
| 169 MenuController::SELECTION_UPDATE_IMMEDIATELY); | |
| 170 } | |
| 171 | |
| 172 void IncrementSelection(int delta) { | |
| 173 controller_->IncrementSelection(delta); | |
| 174 } | |
| 175 | |
| 176 MenuItemView* FindFirstSelectableMenuItem(MenuItemView* parent) { | |
| 177 return controller_->FindFirstSelectableMenuItem(parent); | |
| 178 } | |
| 179 | |
| 180 MenuItemView* FindNextSelectableMenuItem(MenuItemView* parent, | |
| 181 int index, | |
| 182 int delta) { | |
| 183 return controller_->FindNextSelectableMenuItem(parent, index, delta); | |
| 184 } | |
| 185 void SetupMenu(views::Widget* owner, views::MenuItemView* item) { | |
| 186 ResetMenuController(); | |
| 187 controller_ = new MenuController(nullptr, true, nullptr); | |
| 188 controller_->owner_ = owner; | |
| 189 controller_->showing_ = true; | |
| 190 controller_->SetSelection(item, | |
| 191 MenuController::SELECTION_UPDATE_IMMEDIATELY); | |
| 192 } | |
| 193 | |
| 194 void RunMenu(views::Widget* owner) { | |
| 195 scoped_ptr<TestMenuItemView> menu_item(new TestMenuItemView); | |
| 196 SetupMenu(owner, menu_item.get()); | |
| 197 controller_->RunMessageLoop(false); | |
| 198 } | |
| 199 | |
| 200 #if defined(USE_X11) | |
| 201 void DispatchEscapeAndExpect(MenuController::ExitType exit_type) { | |
| 202 ui::ScopedXI2Event key_event; | |
| 203 key_event.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_ESCAPE, 0); | |
| 204 event_source_.Dispatch(key_event); | |
| 205 EXPECT_EQ(exit_type, controller_->exit_type()); | |
| 206 controller_->exit_type_ = MenuController::EXIT_ALL; | |
| 207 DispatchEvent(); | |
| 208 } | |
| 209 | |
| 210 void DispatchTouch(int evtype, int id) { | |
| 211 ui::ScopedXI2Event touch_event; | |
| 212 std::vector<ui::Valuator> valuators; | |
| 213 touch_event.InitTouchEvent(1, evtype, id, gfx::Point(10, 10), valuators); | |
| 214 event_source_.Dispatch(touch_event); | |
| 215 DispatchEvent(); | |
| 216 } | |
| 217 #endif | |
| 218 | |
| 219 void DispatchEvent() { | |
| 220 #if defined(USE_X11) | |
| 221 XEvent xevent; | |
| 222 memset(&xevent, 0, sizeof(xevent)); | |
| 223 event_source_.Dispatch(&xevent); | |
| 224 #elif defined(OS_WIN) | |
| 225 MSG msg; | |
| 226 memset(&msg, 0, sizeof(MSG)); | |
| 227 dispatcher_client_.dispatcher()->Dispatch(msg); | |
| 228 #elif defined(USE_OZONE) | |
| 229 ui::KeyEvent event(' ', ui::VKEY_SPACE, ui::EF_NONE); | |
| 230 event_source_.Dispatch(&event); | |
| 231 #else | |
| 232 #error Unsupported platform | |
| 233 #endif | |
| 234 } | |
| 235 | |
| 236 private: | |
| 237 void ResetMenuController() { | |
| 238 if (controller_) { | |
| 239 // These properties are faked by RunMenu for the purposes of testing and | |
| 240 // need to be undone before we call the destructor. | |
| 241 controller_->owner_ = nullptr; | |
| 242 controller_->showing_ = false; | |
| 243 delete controller_; | |
| 244 controller_ = nullptr; | |
| 245 } | |
| 246 } | |
| 247 | |
| 248 // A weak pointer to the MenuController owned by this class. | |
| 249 MenuController* controller_; | |
| 250 scoped_ptr<base::RunLoop> run_loop_; | |
| 251 TestPlatformEventSource event_source_; | |
| 252 TestDispatcherClient dispatcher_client_; | |
| 253 | |
| 254 DISALLOW_COPY_AND_ASSIGN(MenuControllerTest); | |
| 255 }; | |
| 256 | |
| 257 TEST_F(MenuControllerTest, Basic) { | |
| 258 base::MessageLoop::ScopedNestableTaskAllower allow_nested( | |
| 259 base::MessageLoop::current()); | |
| 260 message_loop()->PostTask( | |
| 261 FROM_HERE, | |
| 262 base::Bind(&MenuControllerTest::Step1_RunMenu, base::Unretained(this))); | |
| 263 } | |
| 264 | |
| 265 #if defined(OS_LINUX) && defined(USE_X11) | |
| 266 // Tests that an event targeter which blocks events will be honored by the menu | |
| 267 // event dispatcher. | |
| 268 TEST_F(MenuControllerTest, EventTargeter) { | |
| 269 { | |
| 270 // Verify that the menu handles the escape key under normal circumstances. | |
| 271 scoped_ptr<Widget> owner(CreateOwnerWidget()); | |
| 272 message_loop()->PostTask( | |
| 273 FROM_HERE, | |
| 274 base::Bind(&MenuControllerTest::DispatchEscapeAndExpect, | |
| 275 base::Unretained(this), | |
| 276 MenuController::EXIT_OUTERMOST)); | |
| 277 RunMenu(owner.get()); | |
| 278 } | |
| 279 | |
| 280 { | |
| 281 // With the NULL targeter instantiated and assigned we expect the menu to | |
| 282 // not handle the key event. | |
| 283 scoped_ptr<Widget> owner(CreateOwnerWidget()); | |
| 284 aura::ScopedWindowTargeter scoped_targeter( | |
| 285 owner->GetNativeWindow()->GetRootWindow(), | |
| 286 scoped_ptr<ui::EventTargeter>(new ui::NullEventTargeter)); | |
| 287 message_loop()->PostTask( | |
| 288 FROM_HERE, | |
| 289 base::Bind(&MenuControllerTest::DispatchEscapeAndExpect, | |
| 290 base::Unretained(this), | |
| 291 MenuController::EXIT_NONE)); | |
| 292 RunMenu(owner.get()); | |
| 293 } | |
| 294 } | |
| 295 #endif | |
| 296 | |
| 297 #if defined(USE_X11) | |
| 298 | |
| 299 class TestEventHandler : public ui::EventHandler { | 38 class TestEventHandler : public ui::EventHandler { |
| 300 public: | 39 public: |
| 301 TestEventHandler() : outstanding_touches_(0) {} | 40 TestEventHandler() : outstanding_touches_(0) {} |
| 302 | 41 |
| 303 void OnTouchEvent(ui::TouchEvent* event) override { | 42 void OnTouchEvent(ui::TouchEvent* event) override { |
| 304 switch(event->type()) { | 43 switch(event->type()) { |
| 305 case ui::ET_TOUCH_PRESSED: | 44 case ui::ET_TOUCH_PRESSED: |
| 306 outstanding_touches_++; | 45 outstanding_touches_++; |
| 307 break; | 46 break; |
| 308 case ui::ET_TOUCH_RELEASED: | 47 case ui::ET_TOUCH_RELEASED: |
| 309 case ui::ET_TOUCH_CANCELLED: | 48 case ui::ET_TOUCH_CANCELLED: |
| 310 outstanding_touches_--; | 49 outstanding_touches_--; |
| 311 break; | 50 break; |
| 312 default: | 51 default: |
| 313 break; | 52 break; |
| 314 } | 53 } |
| 315 } | 54 } |
| 316 | 55 |
| 317 int outstanding_touches() const { return outstanding_touches_; } | 56 int outstanding_touches() const { return outstanding_touches_; } |
| 318 | 57 |
| 319 private: | 58 private: |
| 320 int outstanding_touches_; | 59 int outstanding_touches_; |
| 321 }; | 60 }; |
| 322 | 61 |
| 62 } // namespace | |
| 63 | |
| 64 class MenuControllerTest : public ViewsTestBase { | |
| 65 public: | |
| 66 MenuControllerTest() | |
| 67 : owner_(), | |
| 68 event_generator_(), | |
| 69 menu_item_(), | |
|
oshima
2015/06/12 19:59:18
you can skip these
afakhry
2015/06/12 23:50:46
I know but I always like to be explicit .. however
| |
| 70 menu_controller_(nullptr) { | |
| 71 } | |
| 72 | |
| 73 ~MenuControllerTest() override {} | |
| 74 | |
| 75 void SetUp() override { | |
| 76 ViewsTestBase::SetUp(); | |
| 77 Init(); | |
| 78 } | |
| 79 | |
| 80 void TearDown() override { | |
| 81 if (menu_controller_) { | |
| 82 menu_controller_->owner_ = nullptr; | |
| 83 menu_controller_->showing_ = false; | |
|
oshima
2015/06/12 19:59:18
do you need these?
afakhry
2015/06/12 23:50:46
Yes the destructor will crash on a DCHECK() if |sh
| |
| 84 delete menu_controller_; | |
| 85 menu_controller_ = nullptr; | |
|
oshima
2015/06/12 19:59:18
any reason why we shouldn't use scoped_ptr for thi
afakhry
2015/06/12 23:50:46
Done. Using a scoped_ptr with a specialized Delete
| |
| 86 } | |
| 87 | |
| 88 ViewsTestBase::TearDown(); | |
| 89 } | |
| 90 | |
| 91 void ReleaseTouchId(int id) { | |
| 92 event_generator_->ReleaseTouchId(id); | |
| 93 } | |
| 94 | |
| 95 void PressKey(ui::KeyboardCode key_code) { | |
| 96 event_generator_->PressKey(key_code, 0); | |
| 97 } | |
| 98 | |
| 99 void TestEventTargeter() { | |
| 100 // With the NULL targeter instantiated and assigned we expect the menu to | |
|
oshima
2015/06/12 19:59:18
nullptr
afakhry
2015/06/12 23:50:46
Actually NULL here refers to the ui::NullEventTarg
| |
| 101 // not handle the key event. | |
| 102 scoped_ptr<aura::ScopedWindowTargeter> scoped_targeter( | |
| 103 new aura::ScopedWindowTargeter( | |
| 104 owner()->GetNativeWindow()->GetRootWindow(), | |
| 105 scoped_ptr<ui::EventTargeter>(new ui::NullEventTargeter))); | |
| 106 event_generator_->PressKey(ui::VKEY_ESCAPE, 0); | |
| 107 EXPECT_EQ(MenuController::EXIT_NONE, menu_exit_type()); | |
| 108 | |
| 109 // Now remove the targeter an expect to exit the menu normally. | |
| 110 scoped_targeter.reset(); | |
| 111 event_generator_->PressKey(ui::VKEY_ESCAPE, 0); | |
| 112 EXPECT_EQ(MenuController::EXIT_OUTERMOST, menu_exit_type()); | |
| 113 } | |
| 114 | |
| 115 protected: | |
| 116 void SetPendingStateItem(MenuItemView* item) { | |
| 117 menu_controller_->pending_state_.item = item; | |
| 118 } | |
| 119 | |
| 120 void ResetSelection() { | |
| 121 menu_controller_->SetSelection( | |
| 122 nullptr, | |
| 123 MenuController::SELECTION_EXIT | | |
| 124 MenuController::SELECTION_UPDATE_IMMEDIATELY); | |
| 125 } | |
| 126 | |
| 127 void IncrementSelection(int delta) { | |
| 128 menu_controller_->IncrementSelection(delta); | |
| 129 } | |
| 130 | |
| 131 MenuItemView* FindFirstSelectableMenuItem(MenuItemView* parent) { | |
| 132 return menu_controller_->FindFirstSelectableMenuItem(parent); | |
| 133 } | |
| 134 | |
| 135 MenuItemView* FindNextSelectableMenuItem(MenuItemView* parent, | |
| 136 int index, | |
| 137 int delta) { | |
| 138 | |
| 139 return menu_controller_->FindNextSelectableMenuItem(parent, index, delta); | |
| 140 } | |
| 141 | |
| 142 void RunMenu() { | |
| 143 menu_controller_->RunMessageLoop(false); | |
| 144 } | |
| 145 | |
| 146 Widget* owner() { return owner_.get(); } | |
| 147 ui::test::EventGenerator* event_generator() { return event_generator_.get(); } | |
| 148 TestMenuItemView* menu_item() { return menu_item_.get(); } | |
| 149 MenuController* menu_controller() { return menu_controller_; } | |
| 150 const MenuItemView* pending_state_item() const { | |
| 151 return menu_controller_->pending_state_.item; | |
| 152 } | |
| 153 const MenuController::ExitType menu_exit_type() const { | |
| 154 return menu_controller_->exit_type_; | |
| 155 } | |
| 156 | |
| 157 private: | |
| 158 void Init() { | |
| 159 owner_.reset(new Widget); | |
| 160 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); | |
| 161 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
| 162 owner_->Init(params); | |
| 163 event_generator_.reset(new ui::test::EventGenerator( | |
| 164 owner_->GetNativeWindow()->GetRootWindow(), owner_->GetNativeWindow())); | |
| 165 owner_->Show(); | |
| 166 | |
| 167 SetupMenuItem(); | |
| 168 | |
| 169 SetupMenuController(); | |
| 170 } | |
| 171 | |
| 172 void SetupMenuItem() { | |
| 173 menu_item_.reset(new TestMenuItemView); | |
| 174 menu_item_->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One")); | |
| 175 menu_item_->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("Two")); | |
| 176 menu_item_->AppendMenuItemWithLabel(3, base::ASCIIToUTF16("Three")); | |
| 177 menu_item_->AppendMenuItemWithLabel(4, base::ASCIIToUTF16("Four")); | |
| 178 } | |
| 179 | |
| 180 void SetupMenuController() { | |
| 181 menu_controller_ = new MenuController(nullptr, true, nullptr); | |
| 182 menu_controller_->owner_ = owner_.get(); | |
| 183 menu_controller_->showing_ = true; | |
| 184 menu_controller_->SetSelection( | |
| 185 menu_item_.get(), MenuController::SELECTION_UPDATE_IMMEDIATELY); | |
| 186 } | |
| 187 | |
| 188 scoped_ptr<Widget> owner_; | |
| 189 scoped_ptr<ui::test::EventGenerator> event_generator_; | |
| 190 scoped_ptr<TestMenuItemView> menu_item_; | |
| 191 MenuController* menu_controller_; | |
| 192 | |
| 193 DISALLOW_COPY_AND_ASSIGN(MenuControllerTest); | |
| 194 }; | |
| 195 | |
| 196 // Tests that an event targeter which blocks events will be honored by the menu | |
| 197 // event dispatcher. | |
| 198 TEST_F(MenuControllerTest, EventTargeter) { | |
| 199 base::MessageLoopForUI::current()->PostTask( | |
| 200 FROM_HERE, | |
| 201 base::Bind(&MenuControllerTest::TestEventTargeter, | |
| 202 base::Unretained(this))); | |
| 203 RunMenu(); | |
| 204 } | |
| 205 | |
| 206 #if defined(USE_X11) | |
| 207 | |
| 323 // Tests that touch event ids are released correctly. See | 208 // Tests that touch event ids are released correctly. See |
| 324 // crbug.com/439051 for details. When the ids aren't managed | 209 // crbug.com/439051 for details. When the ids aren't managed |
| 325 // correctly, we get stuck down touches. | 210 // correctly, we get stuck down touches. |
| 326 TEST_F(MenuControllerTest, TouchIdsReleasedCorrectly) { | 211 TEST_F(MenuControllerTest, TouchIdsReleasedCorrectly) { |
| 327 scoped_ptr<Widget> owner(CreateOwnerWidget()); | |
| 328 TestEventHandler test_event_handler; | 212 TestEventHandler test_event_handler; |
| 329 owner->GetNativeWindow()->GetRootWindow()->AddPreTargetHandler( | 213 owner()->GetNativeWindow()->GetRootWindow()->AddPreTargetHandler( |
| 330 &test_event_handler); | 214 &test_event_handler); |
| 331 | 215 |
| 332 std::vector<int> devices; | 216 std::vector<int> devices; |
| 333 devices.push_back(1); | 217 devices.push_back(1); |
| 334 ui::SetUpTouchDevicesForTest(devices); | 218 ui::SetUpTouchDevicesForTest(devices); |
| 335 | 219 |
| 336 DispatchTouch(XI_TouchBegin, 0); | 220 event_generator()->PressTouchId(0); |
| 337 DispatchTouch(XI_TouchBegin, 1); | 221 event_generator()->PressTouchId(1); |
| 338 DispatchTouch(XI_TouchEnd, 0); | 222 event_generator()->ReleaseTouchId(0); |
| 339 | 223 |
| 340 message_loop()->PostTask(FROM_HERE, | 224 base::MessageLoopForUI::current()->PostTask( |
| 341 base::Bind(&MenuControllerTest::DispatchTouch, | |
| 342 base::Unretained(this), XI_TouchEnd, 1)); | |
| 343 | |
| 344 message_loop()->PostTask( | |
| 345 FROM_HERE, | 225 FROM_HERE, |
| 346 base::Bind(&MenuControllerTest::DispatchEscapeAndExpect, | 226 base::Bind(&MenuControllerTest::ReleaseTouchId, |
| 347 base::Unretained(this), MenuController::EXIT_OUTERMOST)); | 227 base::Unretained(this), |
| 348 | 228 1)); |
| 349 RunMenu(owner.get()); | 229 |
| 230 base::MessageLoopForUI::current()->PostTask( | |
| 231 FROM_HERE, | |
| 232 base::Bind(&MenuControllerTest::PressKey, | |
| 233 base::Unretained(this), | |
| 234 ui::VKEY_ESCAPE)); | |
| 235 | |
| 236 RunMenu(); | |
| 237 | |
| 238 EXPECT_EQ(MenuController::EXIT_OUTERMOST, menu_exit_type()); | |
| 350 EXPECT_EQ(0, test_event_handler.outstanding_touches()); | 239 EXPECT_EQ(0, test_event_handler.outstanding_touches()); |
| 351 | 240 |
| 352 owner->GetNativeWindow()->GetRootWindow()->RemovePreTargetHandler( | 241 owner()->GetNativeWindow()->GetRootWindow()->RemovePreTargetHandler( |
| 353 &test_event_handler); | 242 &test_event_handler); |
| 354 } | 243 } |
| 355 #endif // defined(USE_X11) | 244 |
| 245 #endif // defined(USE_X11) | |
| 356 | 246 |
| 357 TEST_F(MenuControllerTest, FirstSelectedItem) { | 247 TEST_F(MenuControllerTest, FirstSelectedItem) { |
| 358 scoped_ptr<Widget> owner(CreateOwnerWidget()); | 248 // disabling all items but "Two". |
| 359 scoped_ptr<TestMenuItemView> menu_item(new TestMenuItemView); | 249 menu_item()->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false); |
| 360 menu_item->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One")); | 250 menu_item()->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); |
| 361 menu_item->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("Two")); | 251 menu_item()->GetSubmenu()->GetMenuItemAt(3)->SetEnabled(false); |
| 362 // Disabling the item "One" gets it skipped when a menu is first opened. | 252 |
| 363 menu_item->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false); | 253 // "Two" should be the first selectable item. |
| 364 | 254 MenuItemView* first_selectable = FindFirstSelectableMenuItem(menu_item()); |
| 365 SetupMenu(owner.get(), menu_item.get()); | |
| 366 | |
| 367 // First selectable item should be item "Two". | |
| 368 MenuItemView* first_selectable = FindFirstSelectableMenuItem(menu_item.get()); | |
| 369 EXPECT_EQ(2, first_selectable->GetCommand()); | 255 EXPECT_EQ(2, first_selectable->GetCommand()); |
| 370 | 256 |
| 371 // There should be no next or previous selectable item since there is only a | 257 // There should be no next or previous selectable item since there is only a |
| 372 // single enabled item in the menu. | 258 // single enabled item in the menu. |
| 373 SetPendingStateItem(first_selectable); | 259 SetPendingStateItem(first_selectable); |
| 374 EXPECT_EQ(nullptr, FindNextSelectableMenuItem(menu_item.get(), 1, 1)); | 260 EXPECT_EQ(nullptr, FindNextSelectableMenuItem(menu_item(), 1, 1)); |
| 375 EXPECT_EQ(nullptr, FindNextSelectableMenuItem(menu_item.get(), 1, -1)); | 261 EXPECT_EQ(nullptr, FindNextSelectableMenuItem(menu_item(), 1, -1)); |
| 376 | 262 |
| 377 // Clear references in menu controller to the menu item that is going away. | 263 // Clear references in menu controller to the menu item that is going away. |
| 378 ResetSelection(); | 264 ResetSelection(); |
| 379 } | 265 } |
| 380 | 266 |
| 381 TEST_F(MenuControllerTest, NextSelectedItem) { | 267 TEST_F(MenuControllerTest, NextSelectedItem) { |
| 382 scoped_ptr<Widget> owner(CreateOwnerWidget()); | |
| 383 scoped_ptr<TestMenuItemView> menu_item(new TestMenuItemView); | |
| 384 menu_item->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One")); | |
| 385 menu_item->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("Two")); | |
| 386 menu_item->AppendMenuItemWithLabel(3, base::ASCIIToUTF16("Three")); | |
| 387 menu_item->AppendMenuItemWithLabel(4, base::ASCIIToUTF16("Four")); | |
| 388 // Disabling the item "Three" gets it skipped when using keyboard to navigate. | 268 // Disabling the item "Three" gets it skipped when using keyboard to navigate. |
| 389 menu_item->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); | 269 menu_item()->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); |
| 390 | |
| 391 SetupMenu(owner.get(), menu_item.get()); | |
| 392 | 270 |
| 393 // Fake initial hot selection. | 271 // Fake initial hot selection. |
| 394 SetPendingStateItem(menu_item->GetSubmenu()->GetMenuItemAt(0)); | 272 SetPendingStateItem(menu_item()->GetSubmenu()->GetMenuItemAt(0)); |
| 395 EXPECT_EQ(1, pending_state_item()->GetCommand()); | 273 EXPECT_EQ(1, pending_state_item()->GetCommand()); |
| 396 | 274 |
| 397 // Move down in the menu. | 275 // Move down in the menu. |
| 398 // Select next item. | 276 // Select next item. |
| 399 IncrementSelection(1); | 277 IncrementSelection(1); |
| 400 EXPECT_EQ(2, pending_state_item()->GetCommand()); | 278 EXPECT_EQ(2, pending_state_item()->GetCommand()); |
| 401 | 279 |
| 402 // Skip disabled item. | 280 // Skip disabled item. |
| 403 IncrementSelection(1); | 281 IncrementSelection(1); |
| 404 EXPECT_EQ(4, pending_state_item()->GetCommand()); | 282 EXPECT_EQ(4, pending_state_item()->GetCommand()); |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 418 | 296 |
| 419 // Select previous item. | 297 // Select previous item. |
| 420 IncrementSelection(-1); | 298 IncrementSelection(-1); |
| 421 EXPECT_EQ(1, pending_state_item()->GetCommand()); | 299 EXPECT_EQ(1, pending_state_item()->GetCommand()); |
| 422 | 300 |
| 423 // Clear references in menu controller to the menu item that is going away. | 301 // Clear references in menu controller to the menu item that is going away. |
| 424 ResetSelection(); | 302 ResetSelection(); |
| 425 } | 303 } |
| 426 | 304 |
| 427 } // namespace views | 305 } // namespace views |
| OLD | NEW |