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" |
| 8 #include "ui/aura/scoped_window_targeter.h" |
| 9 #include "ui/aura/window.h" |
9 #include "ui/events/event_handler.h" | 10 #include "ui/events/event_handler.h" |
10 #include "ui/events/null_event_targeter.h" | 11 #include "ui/events/null_event_targeter.h" |
11 #include "ui/events/platform/platform_event_source.h" | 12 #include "ui/events/test/event_generator.h" |
12 #include "ui/events/test/platform_event_source_test_api.h" | |
13 #include "ui/views/controls/menu/menu_item_view.h" | 13 #include "ui/views/controls/menu/menu_item_view.h" |
14 #include "ui/views/controls/menu/submenu_view.h" | 14 #include "ui/views/controls/menu/submenu_view.h" |
15 #include "ui/views/test/views_test_base.h" | 15 #include "ui/views/test/views_test_base.h" |
16 | 16 |
17 #if defined(USE_AURA) | 17 #if defined(USE_AURA) |
18 #include "ui/aura/env.h" | |
19 #include "ui/aura/scoped_window_targeter.h" | 18 #include "ui/aura/scoped_window_targeter.h" |
20 #include "ui/aura/window.h" | 19 #include "ui/aura/window.h" |
21 #include "ui/wm/public/dispatcher_client.h" | |
22 #endif | 20 #endif |
23 | 21 |
24 #if defined(OS_WIN) | 22 #if defined(USE_X11) |
25 #include "base/message_loop/message_pump_dispatcher.h" | |
26 #elif defined(USE_X11) | |
27 #include <X11/Xlib.h> | 23 #include <X11/Xlib.h> |
28 #undef Bool | 24 #undef Bool |
29 #undef None | 25 #undef None |
30 #include "ui/events/test/events_test_utils_x11.h" | 26 #include "ui/events/test/events_test_utils_x11.h" |
31 #elif defined(USE_OZONE) | |
32 #include "ui/events/event.h" | |
33 #elif defined(OS_MACOSX) | |
34 #include "ui/events/test/event_generator.h" | |
35 #endif | 27 #endif |
36 | 28 |
37 namespace views { | 29 namespace views { |
38 namespace test { | 30 namespace test { |
39 | 31 |
40 namespace { | 32 namespace { |
41 | 33 |
42 class TestMenuItemView : public MenuItemView { | |
43 public: | |
44 TestMenuItemView() : MenuItemView(nullptr) {} | |
45 ~TestMenuItemView() override {} | |
46 | |
47 private: | |
48 DISALLOW_COPY_AND_ASSIGN(TestMenuItemView); | |
49 }; | |
50 | |
51 class SubmenuViewShown : public SubmenuView { | 34 class SubmenuViewShown : public SubmenuView { |
52 public: | 35 public: |
53 SubmenuViewShown(MenuItemView* parent) : SubmenuView(parent) {} | 36 SubmenuViewShown(MenuItemView* parent) : SubmenuView(parent) {} |
54 ~SubmenuViewShown() override {} | 37 ~SubmenuViewShown() override {} |
55 bool IsShowing() override { return true; } | 38 bool IsShowing() override { return true; } |
56 | 39 |
57 private: | 40 private: |
58 DISALLOW_COPY_AND_ASSIGN(SubmenuViewShown); | 41 DISALLOW_COPY_AND_ASSIGN(SubmenuViewShown); |
59 }; | 42 }; |
60 | 43 |
61 #if defined(USE_AURA) | 44 class TestEventHandler : public ui::EventHandler { |
62 class TestDispatcherClient : public aura::client::DispatcherClient { | |
63 public: | 45 public: |
64 TestDispatcherClient() : dispatcher_(nullptr) {} | 46 TestEventHandler() : outstanding_touches_(0) {} |
65 ~TestDispatcherClient() override {} | |
66 | 47 |
67 base::MessagePumpDispatcher* dispatcher() { | 48 void OnTouchEvent(ui::TouchEvent* event) override { |
68 return dispatcher_; | 49 switch(event->type()) { |
| 50 case ui::ET_TOUCH_PRESSED: |
| 51 outstanding_touches_++; |
| 52 break; |
| 53 case ui::ET_TOUCH_RELEASED: |
| 54 case ui::ET_TOUCH_CANCELLED: |
| 55 outstanding_touches_--; |
| 56 break; |
| 57 default: |
| 58 break; |
| 59 } |
69 } | 60 } |
70 | 61 |
71 // aura::client::DispatcherClient: | 62 int outstanding_touches() const { return outstanding_touches_; } |
72 void PrepareNestedLoopClosures(base::MessagePumpDispatcher* dispatcher, | |
73 base::Closure* run_closure, | |
74 base::Closure* quit_closure) override { | |
75 scoped_ptr<base::RunLoop> run_loop(new base::RunLoop()); | |
76 *quit_closure = run_loop->QuitClosure(); | |
77 *run_closure = base::Bind(&TestDispatcherClient::RunNestedDispatcher, | |
78 base::Unretained(this), | |
79 base::Passed(&run_loop), | |
80 dispatcher); | |
81 } | |
82 | 63 |
83 private: | 64 private: |
84 void RunNestedDispatcher(scoped_ptr<base::RunLoop> run_loop, | 65 int outstanding_touches_; |
85 base::MessagePumpDispatcher* dispatcher) { | 66 DISALLOW_COPY_AND_ASSIGN(TestEventHandler); |
86 base::AutoReset<base::MessagePumpDispatcher*> reset_dispatcher(&dispatcher_, | |
87 dispatcher); | |
88 base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); | |
89 base::MessageLoop::ScopedNestableTaskAllower allow(loop); | |
90 run_loop->Run(); | |
91 } | |
92 | |
93 base::MessagePumpDispatcher* dispatcher_; | |
94 | |
95 DISALLOW_COPY_AND_ASSIGN(TestDispatcherClient); | |
96 }; | 67 }; |
97 #endif // USE_AURA | |
98 | 68 |
99 } // namespace | 69 } // namespace |
100 | 70 |
101 class TestMenuItemViewShown : public MenuItemView { | 71 class TestMenuItemViewShown : public MenuItemView { |
102 public: | 72 public: |
103 TestMenuItemViewShown() : MenuItemView(nullptr) { | 73 TestMenuItemViewShown() : MenuItemView(nullptr) { |
104 submenu_ = new SubmenuViewShown(this); | 74 submenu_ = new SubmenuViewShown(this); |
105 } | 75 } |
106 ~TestMenuItemViewShown() override {} | 76 ~TestMenuItemViewShown() override {} |
107 | 77 |
108 private: | 78 private: |
109 DISALLOW_COPY_AND_ASSIGN(TestMenuItemViewShown); | 79 DISALLOW_COPY_AND_ASSIGN(TestMenuItemViewShown); |
110 }; | 80 }; |
111 | 81 |
112 class MenuControllerTest : public ViewsTestBase { | 82 class MenuControllerTest : public ViewsTestBase { |
113 public: | 83 public: |
114 MenuControllerTest() | 84 MenuControllerTest() : menu_controller_(nullptr) { |
115 : controller_(nullptr), | 85 } |
116 event_source_(ui::PlatformEventSource::GetInstance()) {} | 86 |
117 ~MenuControllerTest() override { ResetMenuController(); } | 87 ~MenuControllerTest() override {} |
118 | 88 |
119 // Dispatches |count| number of items, each in a separate iteration of the | 89 // ViewsTestBase: |
120 // message-loop, by posting a task. | 90 void SetUp() override { |
121 void Step3_DispatchEvents(int count) { | 91 ViewsTestBase::SetUp(); |
122 base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); | 92 Init(); |
123 base::MessageLoop::ScopedNestableTaskAllower allow(loop); | 93 } |
124 controller_->exit_type_ = MenuController::EXIT_ALL; | 94 |
125 | 95 void TearDown() override { |
126 DispatchEvent(); | 96 owner_->CloseNow(); |
127 if (count) { | 97 |
128 base::MessageLoop::current()->PostTask( | 98 menu_controller_->showing_ = false; |
129 FROM_HERE, | 99 menu_controller_->owner_ = nullptr; |
130 base::Bind(&MenuControllerTest::Step3_DispatchEvents, | 100 delete menu_controller_; |
131 base::Unretained(this), | 101 menu_controller_ = nullptr; |
132 count - 1)); | 102 |
133 } else { | 103 ViewsTestBase::TearDown(); |
134 EXPECT_TRUE(run_loop_->running()); | 104 } |
135 run_loop_->Quit(); | 105 |
| 106 void ReleaseTouchId(int id) { |
| 107 event_generator_->ReleaseTouchId(id); |
| 108 } |
| 109 |
| 110 void PressKey(ui::KeyboardCode key_code) { |
| 111 event_generator_->PressKey(key_code, 0); |
| 112 } |
| 113 |
| 114 #if defined(OS_LINUX) && defined(USE_X11) |
| 115 void TestEventTargeter() { |
| 116 { |
| 117 // With the |ui::NullEventTargeter| instantiated and assigned we expect |
| 118 // the menu to not handle the key event. |
| 119 aura::ScopedWindowTargeter scoped_targeter( |
| 120 owner()->GetNativeWindow()->GetRootWindow(), |
| 121 scoped_ptr<ui::EventTargeter>(new ui::NullEventTargeter)); |
| 122 event_generator_->PressKey(ui::VKEY_ESCAPE, 0); |
| 123 EXPECT_EQ(MenuController::EXIT_NONE, menu_exit_type()); |
136 } | 124 } |
137 } | 125 // Now that the targeter has been destroyed, expect to exit the menu |
138 | 126 // normally when hitting escape. |
139 // Runs a nested message-loop that does not involve the menu itself. | 127 event_generator_->PressKey(ui::VKEY_ESCAPE, 0); |
140 void Step2_RunNestedLoop() { | 128 EXPECT_EQ(MenuController::EXIT_OUTERMOST, menu_exit_type()); |
141 base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); | 129 } |
142 base::MessageLoop::ScopedNestableTaskAllower allow(loop); | 130 #endif // defined(OS_LINUX) && defined(USE_X11) |
143 base::MessageLoop::current()->PostTask( | 131 |
144 FROM_HERE, | 132 protected: |
145 base::Bind(&MenuControllerTest::Step3_DispatchEvents, | 133 void SetPendingStateItem(MenuItemView* item) { |
146 base::Unretained(this), | 134 menu_controller_->pending_state_.item = item; |
147 3)); | 135 menu_controller_->pending_state_.submenu_open = true; |
148 run_loop_.reset(new base::RunLoop()); | 136 } |
149 run_loop_->Run(); | 137 |
150 } | 138 void ResetSelection() { |
151 | 139 menu_controller_->SetSelection( |
152 void Step1_RunMenu() { | 140 nullptr, |
153 base::MessageLoop::current()->PostTask( | 141 MenuController::SELECTION_EXIT | |
154 FROM_HERE, | 142 MenuController::SELECTION_UPDATE_IMMEDIATELY); |
155 base::Bind(&MenuControllerTest::Step2_RunNestedLoop, | 143 } |
156 base::Unretained(this))); | 144 |
157 scoped_ptr<Widget> owner(CreateOwnerWidget()); | 145 void IncrementSelection() { |
158 RunMenu(owner.get()); | 146 menu_controller_->IncrementSelection( |
159 } | 147 MenuController::INCREMENT_SELECTION_DOWN); |
160 | 148 } |
161 scoped_ptr<Widget> CreateOwnerWidget() { | 149 |
162 scoped_ptr<Widget> widget(new Widget); | 150 void DecrementSelection() { |
| 151 menu_controller_->IncrementSelection( |
| 152 MenuController::INCREMENT_SELECTION_UP); |
| 153 } |
| 154 |
| 155 MenuItemView* FindInitialSelectableMenuItemDown(MenuItemView* parent) { |
| 156 return menu_controller_->FindInitialSelectableMenuItem( |
| 157 parent, MenuController::INCREMENT_SELECTION_DOWN); |
| 158 } |
| 159 |
| 160 MenuItemView* FindInitialSelectableMenuItemUp(MenuItemView* parent) { |
| 161 return menu_controller_->FindInitialSelectableMenuItem( |
| 162 parent, MenuController::INCREMENT_SELECTION_UP); |
| 163 } |
| 164 |
| 165 MenuItemView* FindNextSelectableMenuItem(MenuItemView* parent, |
| 166 int index) { |
| 167 |
| 168 return menu_controller_->FindNextSelectableMenuItem( |
| 169 parent, index, MenuController::INCREMENT_SELECTION_DOWN); |
| 170 } |
| 171 |
| 172 MenuItemView* FindPreviousSelectableMenuItem(MenuItemView* parent, |
| 173 int index) { |
| 174 return menu_controller_->FindNextSelectableMenuItem( |
| 175 parent, index, MenuController::INCREMENT_SELECTION_UP); |
| 176 } |
| 177 |
| 178 void RunMenu() { |
| 179 menu_controller_->RunMessageLoop(false); |
| 180 } |
| 181 |
| 182 Widget* owner() { return owner_.get(); } |
| 183 ui::test::EventGenerator* event_generator() { return event_generator_.get(); } |
| 184 TestMenuItemViewShown* menu_item() { return menu_item_.get(); } |
| 185 MenuController* menu_controller() { return menu_controller_; } |
| 186 const MenuItemView* pending_state_item() const { |
| 187 return menu_controller_->pending_state_.item; |
| 188 } |
| 189 MenuController::ExitType menu_exit_type() const { |
| 190 return menu_controller_->exit_type_; |
| 191 } |
| 192 |
| 193 private: |
| 194 void Init() { |
| 195 owner_.reset(new Widget); |
163 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); | 196 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); |
164 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 197 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
165 widget->Init(params); | 198 owner_->Init(params); |
166 widget->Show(); | 199 event_generator_.reset( |
167 | 200 new ui::test::EventGenerator(GetContext(), owner_->GetNativeWindow())); |
168 #if defined(USE_AURA) | 201 owner_->Show(); |
169 aura::client::SetDispatcherClient( | 202 |
170 widget->GetNativeWindow()->GetRootWindow(), &dispatcher_client_); | 203 SetupMenuItem(); |
171 #endif | 204 |
172 return widget.Pass(); | 205 SetupMenuController(); |
173 } | 206 } |
174 | 207 |
175 const MenuItemView* pending_state_item() const { | 208 void SetupMenuItem() { |
176 return controller_->pending_state_.item; | 209 menu_item_.reset(new TestMenuItemViewShown); |
177 } | 210 menu_item_->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One")); |
178 | 211 menu_item_->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("Two")); |
179 void SetPendingStateItem(MenuItemView* item) { | 212 menu_item_->AppendMenuItemWithLabel(3, base::ASCIIToUTF16("Three")); |
180 controller_->pending_state_.item = item; | 213 menu_item_->AppendMenuItemWithLabel(4, base::ASCIIToUTF16("Four")); |
181 controller_->pending_state_.submenu_open = true; | 214 } |
182 } | 215 |
183 | 216 void SetupMenuController() { |
184 void ResetSelection() { | 217 menu_controller_= new MenuController(nullptr, true, nullptr); |
185 controller_->SetSelection(nullptr, | 218 menu_controller_->owner_ = owner_.get(); |
186 MenuController::SELECTION_EXIT | | 219 menu_controller_->showing_ = true; |
187 MenuController::SELECTION_UPDATE_IMMEDIATELY); | 220 menu_controller_->SetSelection( |
188 } | 221 menu_item_.get(), MenuController::SELECTION_UPDATE_IMMEDIATELY); |
189 | 222 } |
190 void IncrementSelection() { | 223 |
191 controller_->IncrementSelection(MenuController::INCREMENT_SELECTION_DOWN); | 224 scoped_ptr<Widget> owner_; |
192 } | 225 scoped_ptr<ui::test::EventGenerator> event_generator_; |
193 | 226 scoped_ptr<TestMenuItemViewShown> menu_item_; |
194 void DecrementSelection() { | 227 MenuController* menu_controller_; |
195 controller_->IncrementSelection(MenuController::INCREMENT_SELECTION_UP); | |
196 } | |
197 | |
198 MenuItemView* FindInitialSelectableMenuItemDown(MenuItemView* parent) { | |
199 return controller_->FindInitialSelectableMenuItem( | |
200 parent, MenuController::INCREMENT_SELECTION_DOWN); | |
201 } | |
202 | |
203 MenuItemView* FindInitialSelectableMenuItemUp(MenuItemView* parent) { | |
204 return controller_->FindInitialSelectableMenuItem( | |
205 parent, MenuController::INCREMENT_SELECTION_UP); | |
206 } | |
207 | |
208 MenuItemView* FindNextSelectableMenuItem(MenuItemView* parent, | |
209 int index) { | |
210 return controller_->FindNextSelectableMenuItem( | |
211 parent, index, MenuController::INCREMENT_SELECTION_DOWN); | |
212 } | |
213 | |
214 MenuItemView* FindPreviousSelectableMenuItem(MenuItemView* parent, | |
215 int index) { | |
216 return controller_->FindNextSelectableMenuItem( | |
217 parent, index, MenuController::INCREMENT_SELECTION_UP); | |
218 } | |
219 | |
220 void SetupMenu(views::Widget* owner, views::MenuItemView* item) { | |
221 ResetMenuController(); | |
222 controller_ = new MenuController(nullptr, true, nullptr); | |
223 controller_->owner_ = owner; | |
224 controller_->showing_ = true; | |
225 controller_->SetSelection(item, | |
226 MenuController::SELECTION_UPDATE_IMMEDIATELY); | |
227 } | |
228 | |
229 void RunMenu(views::Widget* owner) { | |
230 scoped_ptr<TestMenuItemView> menu_item(new TestMenuItemView); | |
231 SetupMenu(owner, menu_item.get()); | |
232 controller_->RunMessageLoop(false); | |
233 } | |
234 | |
235 #if defined(USE_X11) | |
236 void DispatchEscapeAndExpect(MenuController::ExitType exit_type) { | |
237 ui::ScopedXI2Event key_event; | |
238 key_event.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_ESCAPE, 0); | |
239 event_source_.Dispatch(key_event); | |
240 EXPECT_EQ(exit_type, controller_->exit_type()); | |
241 controller_->exit_type_ = MenuController::EXIT_ALL; | |
242 DispatchEvent(); | |
243 } | |
244 | |
245 void DispatchTouch(int evtype, int id) { | |
246 ui::ScopedXI2Event touch_event; | |
247 std::vector<ui::Valuator> valuators; | |
248 touch_event.InitTouchEvent(1, evtype, id, gfx::Point(10, 10), valuators); | |
249 event_source_.Dispatch(touch_event); | |
250 DispatchEvent(); | |
251 } | |
252 #endif | |
253 | |
254 void DispatchEvent() { | |
255 #if defined(USE_X11) | |
256 XEvent xevent; | |
257 memset(&xevent, 0, sizeof(xevent)); | |
258 event_source_.Dispatch(&xevent); | |
259 #elif defined(OS_WIN) | |
260 MSG msg; | |
261 memset(&msg, 0, sizeof(MSG)); | |
262 dispatcher_client_.dispatcher()->Dispatch(msg); | |
263 #elif defined(USE_OZONE) | |
264 ui::KeyEvent event(' ', ui::VKEY_SPACE, ui::EF_NONE); | |
265 event_source_.Dispatch(&event); | |
266 #elif defined(OS_MACOSX) && !defined(USE_AURA) | |
267 // Since this is not an interactive test, on Mac there will be no key | |
268 // window. Any system event will just get ignored, so use the EventGenerator | |
269 // to generate a dummy event. Without Aura, these will be native events. | |
270 gfx::NativeWindow window = controller_->owner()->GetNativeWindow(); | |
271 ui::test::EventGenerator generator(window, window); | |
272 // Send "up", since this will not activate a menu item. But note that the | |
273 // test has already set exit_type_ = EXIT_ALL just before the first call | |
274 // to this function. | |
275 generator.PressKey(ui::VKEY_UP, 0); | |
276 #else | |
277 #error Unsupported platform | |
278 #endif | |
279 } | |
280 | |
281 private: | |
282 void ResetMenuController() { | |
283 if (controller_) { | |
284 // These properties are faked by RunMenu for the purposes of testing and | |
285 // need to be undone before we call the destructor. | |
286 controller_->owner_ = nullptr; | |
287 controller_->showing_ = false; | |
288 delete controller_; | |
289 controller_ = nullptr; | |
290 } | |
291 } | |
292 | |
293 // A weak pointer to the MenuController owned by this class. | |
294 MenuController* controller_; | |
295 scoped_ptr<base::RunLoop> run_loop_; | |
296 ui::test::PlatformEventSourceTestAPI event_source_; | |
297 #if defined(USE_AURA) | |
298 TestDispatcherClient dispatcher_client_; | |
299 #endif | |
300 | 228 |
301 DISALLOW_COPY_AND_ASSIGN(MenuControllerTest); | 229 DISALLOW_COPY_AND_ASSIGN(MenuControllerTest); |
302 }; | 230 }; |
303 | 231 |
304 TEST_F(MenuControllerTest, Basic) { | |
305 base::MessageLoop::ScopedNestableTaskAllower allow_nested( | |
306 base::MessageLoop::current()); | |
307 message_loop()->PostTask( | |
308 FROM_HERE, | |
309 base::Bind(&MenuControllerTest::Step1_RunMenu, base::Unretained(this))); | |
310 } | |
311 | |
312 #if defined(OS_LINUX) && defined(USE_X11) | 232 #if defined(OS_LINUX) && defined(USE_X11) |
313 // Tests that an event targeter which blocks events will be honored by the menu | 233 // Tests that an event targeter which blocks events will be honored by the menu |
314 // event dispatcher. | 234 // event dispatcher. |
315 TEST_F(MenuControllerTest, EventTargeter) { | 235 TEST_F(MenuControllerTest, EventTargeter) { |
316 { | 236 base::MessageLoopForUI::current()->PostTask( |
317 // Verify that the menu handles the escape key under normal circumstances. | 237 FROM_HERE, |
318 scoped_ptr<Widget> owner(CreateOwnerWidget()); | 238 base::Bind(&MenuControllerTest::TestEventTargeter, |
319 message_loop()->PostTask( | 239 base::Unretained(this))); |
320 FROM_HERE, | 240 RunMenu(); |
321 base::Bind(&MenuControllerTest::DispatchEscapeAndExpect, | |
322 base::Unretained(this), | |
323 MenuController::EXIT_OUTERMOST)); | |
324 RunMenu(owner.get()); | |
325 } | |
326 | |
327 { | |
328 // With the NULL targeter instantiated and assigned we expect the menu to | |
329 // not handle the key event. | |
330 scoped_ptr<Widget> owner(CreateOwnerWidget()); | |
331 aura::ScopedWindowTargeter scoped_targeter( | |
332 owner->GetNativeWindow()->GetRootWindow(), | |
333 scoped_ptr<ui::EventTargeter>(new ui::NullEventTargeter)); | |
334 message_loop()->PostTask( | |
335 FROM_HERE, | |
336 base::Bind(&MenuControllerTest::DispatchEscapeAndExpect, | |
337 base::Unretained(this), | |
338 MenuController::EXIT_NONE)); | |
339 RunMenu(owner.get()); | |
340 } | |
341 } | 241 } |
342 #endif | 242 #endif // defined(OS_LINUX) && defined(USE_X11) |
343 | 243 |
344 #if defined(USE_X11) | 244 #if defined(USE_X11) |
345 | |
346 class TestEventHandler : public ui::EventHandler { | |
347 public: | |
348 TestEventHandler() : outstanding_touches_(0) {} | |
349 | |
350 void OnTouchEvent(ui::TouchEvent* event) override { | |
351 switch(event->type()) { | |
352 case ui::ET_TOUCH_PRESSED: | |
353 outstanding_touches_++; | |
354 break; | |
355 case ui::ET_TOUCH_RELEASED: | |
356 case ui::ET_TOUCH_CANCELLED: | |
357 outstanding_touches_--; | |
358 break; | |
359 default: | |
360 break; | |
361 } | |
362 } | |
363 | |
364 int outstanding_touches() const { return outstanding_touches_; } | |
365 | |
366 private: | |
367 int outstanding_touches_; | |
368 }; | |
369 | |
370 // Tests that touch event ids are released correctly. See crbug.com/439051 for | 245 // Tests that touch event ids are released correctly. See crbug.com/439051 for |
371 // details. When the ids aren't managed correctly, we get stuck down touches. | 246 // details. When the ids aren't managed correctly, we get stuck down touches. |
372 TEST_F(MenuControllerTest, TouchIdsReleasedCorrectly) { | 247 TEST_F(MenuControllerTest, TouchIdsReleasedCorrectly) { |
373 scoped_ptr<Widget> owner(CreateOwnerWidget()); | |
374 TestEventHandler test_event_handler; | 248 TestEventHandler test_event_handler; |
375 owner->GetNativeWindow()->GetRootWindow()->AddPreTargetHandler( | 249 owner()->GetNativeWindow()->GetRootWindow()->AddPreTargetHandler( |
376 &test_event_handler); | 250 &test_event_handler); |
377 | 251 |
378 std::vector<int> devices; | 252 std::vector<int> devices; |
379 devices.push_back(1); | 253 devices.push_back(1); |
380 ui::SetUpTouchDevicesForTest(devices); | 254 ui::SetUpTouchDevicesForTest(devices); |
381 | 255 |
382 DispatchTouch(XI_TouchBegin, 0); | 256 event_generator()->PressTouchId(0); |
383 DispatchTouch(XI_TouchBegin, 1); | 257 event_generator()->PressTouchId(1); |
384 DispatchTouch(XI_TouchEnd, 0); | 258 event_generator()->ReleaseTouchId(0); |
385 | 259 |
386 message_loop()->PostTask(FROM_HERE, | 260 base::MessageLoopForUI::current()->PostTask( |
387 base::Bind(&MenuControllerTest::DispatchTouch, | |
388 base::Unretained(this), XI_TouchEnd, 1)); | |
389 | |
390 message_loop()->PostTask( | |
391 FROM_HERE, | 261 FROM_HERE, |
392 base::Bind(&MenuControllerTest::DispatchEscapeAndExpect, | 262 base::Bind(&MenuControllerTest::ReleaseTouchId, |
393 base::Unretained(this), MenuController::EXIT_OUTERMOST)); | 263 base::Unretained(this), |
394 | 264 1)); |
395 RunMenu(owner.get()); | 265 |
| 266 base::MessageLoopForUI::current()->PostTask( |
| 267 FROM_HERE, |
| 268 base::Bind(&MenuControllerTest::PressKey, |
| 269 base::Unretained(this), |
| 270 ui::VKEY_ESCAPE)); |
| 271 |
| 272 RunMenu(); |
| 273 |
| 274 EXPECT_EQ(MenuController::EXIT_OUTERMOST, menu_exit_type()); |
396 EXPECT_EQ(0, test_event_handler.outstanding_touches()); | 275 EXPECT_EQ(0, test_event_handler.outstanding_touches()); |
397 | 276 |
398 owner->GetNativeWindow()->GetRootWindow()->RemovePreTargetHandler( | 277 owner()->GetNativeWindow()->GetRootWindow()->RemovePreTargetHandler( |
399 &test_event_handler); | 278 &test_event_handler); |
400 } | 279 } |
401 #endif // defined(USE_X11) | 280 #endif // defined(USE_X11) |
402 | 281 |
403 // Tests that initial selected menu items are correct when items are enabled or | 282 // Tests that initial selected menu items are correct when items are enabled or |
404 // disabled. | 283 // disabled. |
405 TEST_F(MenuControllerTest, InitialSelectedItem) { | 284 TEST_F(MenuControllerTest, InitialSelectedItem) { |
406 scoped_ptr<Widget> owner(CreateOwnerWidget()); | 285 // Leave items "Two", "Three", and "Four" enabled. |
407 scoped_ptr<TestMenuItemView> menu_item(new TestMenuItemView); | 286 menu_item()->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false); |
408 menu_item->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One")); | |
409 menu_item->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("Two")); | |
410 menu_item->AppendMenuItemWithLabel(3, base::ASCIIToUTF16("Three")); | |
411 SetupMenu(owner.get(), menu_item.get()); | |
412 | |
413 // Leave items "Two" and "Three" enabled. | |
414 menu_item->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false); | |
415 // The first selectable item should be item "Two". | 287 // The first selectable item should be item "Two". |
416 MenuItemView* first_selectable = | 288 MenuItemView* first_selectable = |
417 FindInitialSelectableMenuItemDown(menu_item.get()); | 289 FindInitialSelectableMenuItemDown(menu_item()); |
418 ASSERT_NE(nullptr, first_selectable); | 290 ASSERT_NE(nullptr, first_selectable); |
419 EXPECT_EQ(2, first_selectable->GetCommand()); | 291 EXPECT_EQ(2, first_selectable->GetCommand()); |
420 // The last selectable item should be item "Three". | 292 // The last selectable item should be item "Four". |
421 MenuItemView* last_selectable = | 293 MenuItemView* last_selectable = |
422 FindInitialSelectableMenuItemUp(menu_item.get()); | 294 FindInitialSelectableMenuItemUp(menu_item()); |
423 ASSERT_NE(nullptr, last_selectable); | 295 ASSERT_NE(nullptr, last_selectable); |
424 EXPECT_EQ(3, last_selectable->GetCommand()); | 296 EXPECT_EQ(4, last_selectable->GetCommand()); |
425 | 297 |
426 // Leave items "One" and "Two" enabled. | 298 // Leave items "One" and "Two" enabled. |
427 menu_item->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(true); | 299 menu_item()->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(true); |
428 menu_item->GetSubmenu()->GetMenuItemAt(1)->SetEnabled(true); | 300 menu_item()->GetSubmenu()->GetMenuItemAt(1)->SetEnabled(true); |
429 menu_item->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); | 301 menu_item()->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); |
| 302 menu_item()->GetSubmenu()->GetMenuItemAt(3)->SetEnabled(false); |
430 // The first selectable item should be item "One". | 303 // The first selectable item should be item "One". |
431 first_selectable = FindInitialSelectableMenuItemDown(menu_item.get()); | 304 first_selectable = FindInitialSelectableMenuItemDown(menu_item()); |
432 ASSERT_NE(nullptr, first_selectable); | 305 ASSERT_NE(nullptr, first_selectable); |
433 EXPECT_EQ(1, first_selectable->GetCommand()); | 306 EXPECT_EQ(1, first_selectable->GetCommand()); |
434 // The last selectable item should be item "Two". | 307 // The last selectable item should be item "Two". |
435 last_selectable = FindInitialSelectableMenuItemUp(menu_item.get()); | 308 last_selectable = FindInitialSelectableMenuItemUp(menu_item()); |
436 ASSERT_NE(nullptr, last_selectable); | 309 ASSERT_NE(nullptr, last_selectable); |
437 EXPECT_EQ(2, last_selectable->GetCommand()); | 310 EXPECT_EQ(2, last_selectable->GetCommand()); |
438 | 311 |
439 // Leave only a single item "One" enabled. | 312 // Leave only a single item "One" enabled. |
440 menu_item->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(true); | 313 menu_item()->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(true); |
441 menu_item->GetSubmenu()->GetMenuItemAt(1)->SetEnabled(false); | 314 menu_item()->GetSubmenu()->GetMenuItemAt(1)->SetEnabled(false); |
442 menu_item->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); | 315 menu_item()->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); |
| 316 menu_item()->GetSubmenu()->GetMenuItemAt(3)->SetEnabled(false); |
443 // The first selectable item should be item "One". | 317 // The first selectable item should be item "One". |
444 first_selectable = FindInitialSelectableMenuItemDown(menu_item.get()); | 318 first_selectable = FindInitialSelectableMenuItemDown(menu_item()); |
445 ASSERT_NE(nullptr, first_selectable); | 319 ASSERT_NE(nullptr, first_selectable); |
446 EXPECT_EQ(1, first_selectable->GetCommand()); | 320 EXPECT_EQ(1, first_selectable->GetCommand()); |
447 // The last selectable item should be item "One". | 321 // The last selectable item should be item "One". |
448 last_selectable = FindInitialSelectableMenuItemUp(menu_item.get()); | 322 last_selectable = FindInitialSelectableMenuItemUp(menu_item()); |
449 ASSERT_NE(nullptr, last_selectable); | 323 ASSERT_NE(nullptr, last_selectable); |
450 EXPECT_EQ(1, last_selectable->GetCommand()); | 324 EXPECT_EQ(1, last_selectable->GetCommand()); |
451 | 325 |
452 // Leave only a single item "Three" enabled. | 326 // Leave only a single item "Three" enabled. |
453 menu_item->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false); | 327 menu_item()->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false); |
454 menu_item->GetSubmenu()->GetMenuItemAt(1)->SetEnabled(false); | 328 menu_item()->GetSubmenu()->GetMenuItemAt(1)->SetEnabled(false); |
455 menu_item->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(true); | 329 menu_item()->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(true); |
| 330 menu_item()->GetSubmenu()->GetMenuItemAt(3)->SetEnabled(false); |
456 // The first selectable item should be item "Three". | 331 // The first selectable item should be item "Three". |
457 first_selectable = FindInitialSelectableMenuItemDown(menu_item.get()); | 332 first_selectable = FindInitialSelectableMenuItemDown(menu_item()); |
458 ASSERT_NE(nullptr, first_selectable); | 333 ASSERT_NE(nullptr, first_selectable); |
459 EXPECT_EQ(3, first_selectable->GetCommand()); | 334 EXPECT_EQ(3, first_selectable->GetCommand()); |
460 // The last selectable item should be item "Three". | 335 // The last selectable item should be item "Three". |
461 last_selectable = FindInitialSelectableMenuItemUp(menu_item.get()); | 336 last_selectable = FindInitialSelectableMenuItemUp(menu_item()); |
462 ASSERT_NE(nullptr, last_selectable); | 337 ASSERT_NE(nullptr, last_selectable); |
463 EXPECT_EQ(3, last_selectable->GetCommand()); | 338 EXPECT_EQ(3, last_selectable->GetCommand()); |
464 | 339 |
465 // Leave only a single item ("Two") selected. It should be the first and the | 340 // Leave only a single item ("Two") selected. It should be the first and the |
466 // last selectable item. | 341 // last selectable item. |
467 menu_item->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false); | 342 menu_item()->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false); |
468 menu_item->GetSubmenu()->GetMenuItemAt(1)->SetEnabled(true); | 343 menu_item()->GetSubmenu()->GetMenuItemAt(1)->SetEnabled(true); |
469 menu_item->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); | 344 menu_item()->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); |
470 first_selectable = FindInitialSelectableMenuItemDown(menu_item.get()); | 345 menu_item()->GetSubmenu()->GetMenuItemAt(3)->SetEnabled(false); |
| 346 first_selectable = FindInitialSelectableMenuItemDown(menu_item()); |
471 ASSERT_NE(nullptr, first_selectable); | 347 ASSERT_NE(nullptr, first_selectable); |
472 EXPECT_EQ(2, first_selectable->GetCommand()); | 348 EXPECT_EQ(2, first_selectable->GetCommand()); |
473 last_selectable = FindInitialSelectableMenuItemUp(menu_item.get()); | 349 last_selectable = FindInitialSelectableMenuItemUp(menu_item()); |
474 ASSERT_NE(nullptr, last_selectable); | 350 ASSERT_NE(nullptr, last_selectable); |
475 EXPECT_EQ(2, last_selectable->GetCommand()); | 351 EXPECT_EQ(2, last_selectable->GetCommand()); |
476 | 352 |
477 // There should be no next or previous selectable item since there is only a | 353 // There should be no next or previous selectable item since there is only a |
478 // single enabled item in the menu. | 354 // single enabled item in the menu. |
479 EXPECT_EQ(nullptr, FindNextSelectableMenuItem(menu_item.get(), 1)); | 355 EXPECT_EQ(nullptr, FindNextSelectableMenuItem(menu_item(), 1)); |
480 EXPECT_EQ(nullptr, FindPreviousSelectableMenuItem(menu_item.get(), 1)); | 356 EXPECT_EQ(nullptr, FindPreviousSelectableMenuItem(menu_item(), 1)); |
481 | 357 |
482 // Clear references in menu controller to the menu item that is going away. | 358 // Clear references in menu controller to the menu item that is going away. |
483 ResetSelection(); | 359 ResetSelection(); |
484 } | 360 } |
485 | 361 |
486 // Tests that opening menu and pressing 'Down' and 'Up' iterates over enabled | 362 // Tests that opening menu and pressing 'Down' and 'Up' iterates over enabled |
487 // items. | 363 // items. |
488 TEST_F(MenuControllerTest, NextSelectedItem) { | 364 TEST_F(MenuControllerTest, NextSelectedItem) { |
489 scoped_ptr<Widget> owner(CreateOwnerWidget()); | |
490 scoped_ptr<TestMenuItemView> menu_item(new TestMenuItemView); | |
491 menu_item->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One")); | |
492 menu_item->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("Two")); | |
493 menu_item->AppendMenuItemWithLabel(3, base::ASCIIToUTF16("Three")); | |
494 menu_item->AppendMenuItemWithLabel(4, base::ASCIIToUTF16("Four")); | |
495 // Disabling the item "Three" gets it skipped when using keyboard to navigate. | 365 // Disabling the item "Three" gets it skipped when using keyboard to navigate. |
496 menu_item->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); | 366 menu_item()->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); |
497 | |
498 SetupMenu(owner.get(), menu_item.get()); | |
499 | 367 |
500 // Fake initial hot selection. | 368 // Fake initial hot selection. |
501 SetPendingStateItem(menu_item->GetSubmenu()->GetMenuItemAt(0)); | 369 SetPendingStateItem(menu_item()->GetSubmenu()->GetMenuItemAt(0)); |
502 EXPECT_EQ(1, pending_state_item()->GetCommand()); | 370 EXPECT_EQ(1, pending_state_item()->GetCommand()); |
503 | 371 |
504 // Move down in the menu. | 372 // Move down in the menu. |
505 // Select next item. | 373 // Select next item. |
506 IncrementSelection(); | 374 IncrementSelection(); |
507 EXPECT_EQ(2, pending_state_item()->GetCommand()); | 375 EXPECT_EQ(2, pending_state_item()->GetCommand()); |
508 | 376 |
509 // Skip disabled item. | 377 // Skip disabled item. |
510 IncrementSelection(); | 378 IncrementSelection(); |
511 EXPECT_EQ(4, pending_state_item()->GetCommand()); | 379 EXPECT_EQ(4, pending_state_item()->GetCommand()); |
(...skipping 14 matching lines...) Expand all Loading... |
526 // Select previous item. | 394 // Select previous item. |
527 DecrementSelection(); | 395 DecrementSelection(); |
528 EXPECT_EQ(1, pending_state_item()->GetCommand()); | 396 EXPECT_EQ(1, pending_state_item()->GetCommand()); |
529 | 397 |
530 // Clear references in menu controller to the menu item that is going away. | 398 // Clear references in menu controller to the menu item that is going away. |
531 ResetSelection(); | 399 ResetSelection(); |
532 } | 400 } |
533 | 401 |
534 // Tests that opening menu and pressing 'Up' selects the last enabled menu item. | 402 // Tests that opening menu and pressing 'Up' selects the last enabled menu item. |
535 TEST_F(MenuControllerTest, PreviousSelectedItem) { | 403 TEST_F(MenuControllerTest, PreviousSelectedItem) { |
536 scoped_ptr<Widget> owner(CreateOwnerWidget()); | |
537 scoped_ptr<TestMenuItemViewShown> menu_item(new TestMenuItemViewShown); | |
538 menu_item->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One")); | |
539 menu_item->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("Two")); | |
540 menu_item->AppendMenuItemWithLabel(3, base::ASCIIToUTF16("Three")); | |
541 menu_item->AppendMenuItemWithLabel(4, base::ASCIIToUTF16("Four")); | |
542 // Disabling the item "Four" gets it skipped when using keyboard to navigate. | 404 // Disabling the item "Four" gets it skipped when using keyboard to navigate. |
543 menu_item->GetSubmenu()->GetMenuItemAt(3)->SetEnabled(false); | 405 menu_item()->GetSubmenu()->GetMenuItemAt(3)->SetEnabled(false); |
544 | |
545 SetupMenu(owner.get(), menu_item.get()); | |
546 | 406 |
547 // Fake initial root item selection and submenu showing. | 407 // Fake initial root item selection and submenu showing. |
548 SetPendingStateItem(menu_item.get()); | 408 SetPendingStateItem(menu_item()); |
549 EXPECT_EQ(0, pending_state_item()->GetCommand()); | 409 EXPECT_EQ(0, pending_state_item()->GetCommand()); |
550 | 410 |
551 // Move up and select a previous (in our case the last enabled) item. | 411 // Move up and select a previous (in our case the last enabled) item. |
552 DecrementSelection(); | 412 DecrementSelection(); |
553 EXPECT_EQ(3, pending_state_item()->GetCommand()); | 413 EXPECT_EQ(3, pending_state_item()->GetCommand()); |
554 | 414 |
555 // Clear references in menu controller to the menu item that is going away. | 415 // Clear references in menu controller to the menu item that is going away. |
556 ResetSelection(); | 416 ResetSelection(); |
557 } | 417 } |
558 | 418 |
559 } // namespace test | 419 } // namespace test |
560 } // namespace views | 420 } // namespace views |
OLD | NEW |