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 if (menu_controller_) { |
oshima
2015/09/08 21:43:08
do you need this if?
afakhry
2015/09/08 23:34:38
No. removed.
| |
129 FROM_HERE, | 99 menu_controller_->showing_ = false; |
130 base::Bind(&MenuControllerTest::Step3_DispatchEvents, | 100 menu_controller_->owner_ = nullptr; |
131 base::Unretained(this), | 101 delete menu_controller_; |
132 count - 1)); | 102 menu_controller_ = nullptr; |
133 } else { | |
134 EXPECT_TRUE(run_loop_->running()); | |
135 run_loop_->Quit(); | |
136 } | 103 } |
137 } | 104 |
138 | 105 ViewsTestBase::TearDown(); |
139 // Runs a nested message-loop that does not involve the menu itself. | 106 } |
140 void Step2_RunNestedLoop() { | 107 |
141 base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); | 108 void ReleaseTouchId(int id) { |
142 base::MessageLoop::ScopedNestableTaskAllower allow(loop); | 109 event_generator_->ReleaseTouchId(id); |
143 base::MessageLoop::current()->PostTask( | 110 } |
144 FROM_HERE, | 111 |
145 base::Bind(&MenuControllerTest::Step3_DispatchEvents, | 112 void PressKey(ui::KeyboardCode key_code) { |
146 base::Unretained(this), | 113 event_generator_->PressKey(key_code, 0); |
147 3)); | 114 } |
148 run_loop_.reset(new base::RunLoop()); | 115 |
149 run_loop_->Run(); | 116 #if defined(OS_LINUX) && defined(USE_X11) |
150 } | 117 void TestEventTargeter() { |
151 | 118 { |
152 void Step1_RunMenu() { | 119 // With the |ui::NullEventTargeter| instantiated and assigned we expect |
153 base::MessageLoop::current()->PostTask( | 120 // the menu to not handle the key event. |
154 FROM_HERE, | 121 aura::ScopedWindowTargeter scoped_targeter( |
155 base::Bind(&MenuControllerTest::Step2_RunNestedLoop, | 122 owner()->GetNativeWindow()->GetRootWindow(), |
156 base::Unretained(this))); | 123 scoped_ptr<ui::EventTargeter>(new ui::NullEventTargeter)); |
157 scoped_ptr<Widget> owner(CreateOwnerWidget()); | 124 event_generator_->PressKey(ui::VKEY_ESCAPE, 0); |
158 RunMenu(owner.get()); | 125 EXPECT_EQ(MenuController::EXIT_NONE, menu_exit_type()); |
159 } | 126 } |
160 | 127 // Now that the targeter has been destroyed, expect to exit the menu |
161 scoped_ptr<Widget> CreateOwnerWidget() { | 128 // normally when hitting escape. |
162 scoped_ptr<Widget> widget(new Widget); | 129 event_generator_->PressKey(ui::VKEY_ESCAPE, 0); |
130 EXPECT_EQ(MenuController::EXIT_OUTERMOST, menu_exit_type()); | |
131 } | |
132 #endif // defined(OS_LINUX) && defined(USE_X11) | |
133 | |
134 protected: | |
135 void SetPendingStateItem(MenuItemView* item) { | |
136 menu_controller_->pending_state_.item = item; | |
137 menu_controller_->pending_state_.submenu_open = true; | |
138 } | |
139 | |
140 void ResetSelection() { | |
141 menu_controller_->SetSelection( | |
142 nullptr, | |
143 MenuController::SELECTION_EXIT | | |
144 MenuController::SELECTION_UPDATE_IMMEDIATELY); | |
145 } | |
146 | |
147 void IncrementSelection() { | |
148 menu_controller_->IncrementSelection( | |
149 MenuController::INCREMENT_SELECTION_DOWN); | |
150 } | |
151 | |
152 void DecrementSelection() { | |
153 menu_controller_->IncrementSelection( | |
154 MenuController::INCREMENT_SELECTION_UP); | |
155 } | |
156 | |
157 MenuItemView* FindInitialSelectableMenuItemDown(MenuItemView* parent) { | |
158 return menu_controller_->FindInitialSelectableMenuItem( | |
159 parent, MenuController::INCREMENT_SELECTION_DOWN); | |
160 } | |
161 | |
162 MenuItemView* FindInitialSelectableMenuItemUp(MenuItemView* parent) { | |
163 return menu_controller_->FindInitialSelectableMenuItem( | |
164 parent, MenuController::INCREMENT_SELECTION_UP); | |
165 } | |
166 | |
167 MenuItemView* FindNextSelectableMenuItem(MenuItemView* parent, | |
168 int index) { | |
169 | |
170 return menu_controller_->FindNextSelectableMenuItem( | |
171 parent, index, MenuController::INCREMENT_SELECTION_DOWN); | |
172 } | |
173 | |
174 MenuItemView* FindPreviousSelectableMenuItem(MenuItemView* parent, | |
175 int index) { | |
176 return menu_controller_->FindNextSelectableMenuItem( | |
177 parent, index, MenuController::INCREMENT_SELECTION_UP); | |
178 } | |
179 | |
180 void RunMenu() { | |
181 menu_controller_->RunMessageLoop(false); | |
182 } | |
183 | |
184 Widget* owner() { return owner_.get(); } | |
185 ui::test::EventGenerator* event_generator() { return event_generator_.get(); } | |
186 TestMenuItemViewShown* menu_item() { return menu_item_.get(); } | |
187 MenuController* menu_controller() { return menu_controller_; } | |
188 const MenuItemView* pending_state_item() const { | |
189 return menu_controller_->pending_state_.item; | |
190 } | |
191 MenuController::ExitType menu_exit_type() const { | |
192 return menu_controller_->exit_type_; | |
193 } | |
194 | |
195 private: | |
196 void Init() { | |
197 owner_.reset(new Widget); | |
163 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); | 198 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); |
164 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 199 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
165 widget->Init(params); | 200 owner_->Init(params); |
166 widget->Show(); | 201 event_generator_.reset( |
167 | 202 new ui::test::EventGenerator(GetContext(), owner_->GetNativeWindow())); |
168 #if defined(USE_AURA) | 203 owner_->Show(); |
169 aura::client::SetDispatcherClient( | 204 |
170 widget->GetNativeWindow()->GetRootWindow(), &dispatcher_client_); | 205 SetupMenuItem(); |
171 #endif | 206 |
172 return widget.Pass(); | 207 SetupMenuController(); |
173 } | 208 } |
174 | 209 |
175 const MenuItemView* pending_state_item() const { | 210 void SetupMenuItem() { |
176 return controller_->pending_state_.item; | 211 menu_item_.reset(new TestMenuItemViewShown); |
177 } | 212 menu_item_->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One")); |
178 | 213 menu_item_->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("Two")); |
179 void SetPendingStateItem(MenuItemView* item) { | 214 menu_item_->AppendMenuItemWithLabel(3, base::ASCIIToUTF16("Three")); |
180 controller_->pending_state_.item = item; | 215 menu_item_->AppendMenuItemWithLabel(4, base::ASCIIToUTF16("Four")); |
181 controller_->pending_state_.submenu_open = true; | 216 } |
182 } | 217 |
183 | 218 void SetupMenuController() { |
184 void ResetSelection() { | 219 menu_controller_= new MenuController(nullptr, true, nullptr); |
185 controller_->SetSelection(nullptr, | 220 menu_controller_->owner_ = owner_.get(); |
186 MenuController::SELECTION_EXIT | | 221 menu_controller_->showing_ = true; |
187 MenuController::SELECTION_UPDATE_IMMEDIATELY); | 222 menu_controller_->SetSelection( |
188 } | 223 menu_item_.get(), MenuController::SELECTION_UPDATE_IMMEDIATELY); |
189 | 224 } |
190 void IncrementSelection() { | 225 |
191 controller_->IncrementSelection(MenuController::INCREMENT_SELECTION_DOWN); | 226 scoped_ptr<Widget> owner_; |
192 } | 227 scoped_ptr<ui::test::EventGenerator> event_generator_; |
193 | 228 scoped_ptr<TestMenuItemViewShown> menu_item_; |
194 void DecrementSelection() { | 229 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 | 230 |
301 DISALLOW_COPY_AND_ASSIGN(MenuControllerTest); | 231 DISALLOW_COPY_AND_ASSIGN(MenuControllerTest); |
302 }; | 232 }; |
303 | 233 |
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) | 234 #if defined(OS_LINUX) && defined(USE_X11) |
313 // Tests that an event targeter which blocks events will be honored by the menu | 235 // Tests that an event targeter which blocks events will be honored by the menu |
314 // event dispatcher. | 236 // event dispatcher. |
315 TEST_F(MenuControllerTest, EventTargeter) { | 237 TEST_F(MenuControllerTest, EventTargeter) { |
316 { | 238 base::MessageLoopForUI::current()->PostTask( |
317 // Verify that the menu handles the escape key under normal circumstances. | 239 FROM_HERE, |
318 scoped_ptr<Widget> owner(CreateOwnerWidget()); | 240 base::Bind(&MenuControllerTest::TestEventTargeter, |
319 message_loop()->PostTask( | 241 base::Unretained(this))); |
320 FROM_HERE, | 242 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 } | 243 } |
342 #endif | 244 #endif // defined(OS_LINUX) && defined(USE_X11) |
343 | 245 |
344 #if defined(USE_X11) | 246 #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 | 247 // 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. | 248 // details. When the ids aren't managed correctly, we get stuck down touches. |
372 TEST_F(MenuControllerTest, TouchIdsReleasedCorrectly) { | 249 TEST_F(MenuControllerTest, TouchIdsReleasedCorrectly) { |
373 scoped_ptr<Widget> owner(CreateOwnerWidget()); | |
374 TestEventHandler test_event_handler; | 250 TestEventHandler test_event_handler; |
375 owner->GetNativeWindow()->GetRootWindow()->AddPreTargetHandler( | 251 owner()->GetNativeWindow()->GetRootWindow()->AddPreTargetHandler( |
376 &test_event_handler); | 252 &test_event_handler); |
377 | 253 |
378 std::vector<int> devices; | 254 std::vector<int> devices; |
379 devices.push_back(1); | 255 devices.push_back(1); |
380 ui::SetUpTouchDevicesForTest(devices); | 256 ui::SetUpTouchDevicesForTest(devices); |
381 | 257 |
382 DispatchTouch(XI_TouchBegin, 0); | 258 event_generator()->PressTouchId(0); |
383 DispatchTouch(XI_TouchBegin, 1); | 259 event_generator()->PressTouchId(1); |
384 DispatchTouch(XI_TouchEnd, 0); | 260 event_generator()->ReleaseTouchId(0); |
385 | 261 |
386 message_loop()->PostTask(FROM_HERE, | 262 base::MessageLoopForUI::current()->PostTask( |
387 base::Bind(&MenuControllerTest::DispatchTouch, | |
388 base::Unretained(this), XI_TouchEnd, 1)); | |
389 | |
390 message_loop()->PostTask( | |
391 FROM_HERE, | 263 FROM_HERE, |
392 base::Bind(&MenuControllerTest::DispatchEscapeAndExpect, | 264 base::Bind(&MenuControllerTest::ReleaseTouchId, |
393 base::Unretained(this), MenuController::EXIT_OUTERMOST)); | 265 base::Unretained(this), |
394 | 266 1)); |
395 RunMenu(owner.get()); | 267 |
268 base::MessageLoopForUI::current()->PostTask( | |
269 FROM_HERE, | |
270 base::Bind(&MenuControllerTest::PressKey, | |
271 base::Unretained(this), | |
272 ui::VKEY_ESCAPE)); | |
273 | |
274 RunMenu(); | |
275 | |
276 EXPECT_EQ(MenuController::EXIT_OUTERMOST, menu_exit_type()); | |
396 EXPECT_EQ(0, test_event_handler.outstanding_touches()); | 277 EXPECT_EQ(0, test_event_handler.outstanding_touches()); |
397 | 278 |
398 owner->GetNativeWindow()->GetRootWindow()->RemovePreTargetHandler( | 279 owner()->GetNativeWindow()->GetRootWindow()->RemovePreTargetHandler( |
399 &test_event_handler); | 280 &test_event_handler); |
400 } | 281 } |
401 #endif // defined(USE_X11) | 282 #endif // defined(USE_X11) |
402 | 283 |
403 // Tests that initial selected menu items are correct when items are enabled or | 284 // Tests that initial selected menu items are correct when items are enabled or |
404 // disabled. | 285 // disabled. |
405 TEST_F(MenuControllerTest, InitialSelectedItem) { | 286 TEST_F(MenuControllerTest, InitialSelectedItem) { |
406 scoped_ptr<Widget> owner(CreateOwnerWidget()); | 287 // Leave items "Two", "Three", and "Four" enabled. |
407 scoped_ptr<TestMenuItemView> menu_item(new TestMenuItemView); | 288 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". | 289 // The first selectable item should be item "Two". |
416 MenuItemView* first_selectable = | 290 MenuItemView* first_selectable = |
417 FindInitialSelectableMenuItemDown(menu_item.get()); | 291 FindInitialSelectableMenuItemDown(menu_item()); |
418 ASSERT_NE(nullptr, first_selectable); | 292 ASSERT_NE(nullptr, first_selectable); |
419 EXPECT_EQ(2, first_selectable->GetCommand()); | 293 EXPECT_EQ(2, first_selectable->GetCommand()); |
420 // The last selectable item should be item "Three". | 294 // The last selectable item should be item "Four". |
421 MenuItemView* last_selectable = | 295 MenuItemView* last_selectable = |
422 FindInitialSelectableMenuItemUp(menu_item.get()); | 296 FindInitialSelectableMenuItemUp(menu_item()); |
423 ASSERT_NE(nullptr, last_selectable); | 297 ASSERT_NE(nullptr, last_selectable); |
424 EXPECT_EQ(3, last_selectable->GetCommand()); | 298 EXPECT_EQ(4, last_selectable->GetCommand()); |
425 | 299 |
426 // Leave items "One" and "Two" enabled. | 300 // Leave items "One" and "Two" enabled. |
427 menu_item->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(true); | 301 menu_item()->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(true); |
428 menu_item->GetSubmenu()->GetMenuItemAt(1)->SetEnabled(true); | 302 menu_item()->GetSubmenu()->GetMenuItemAt(1)->SetEnabled(true); |
429 menu_item->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); | 303 menu_item()->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); |
304 menu_item()->GetSubmenu()->GetMenuItemAt(3)->SetEnabled(false); | |
430 // The first selectable item should be item "One". | 305 // The first selectable item should be item "One". |
431 first_selectable = FindInitialSelectableMenuItemDown(menu_item.get()); | 306 first_selectable = FindInitialSelectableMenuItemDown(menu_item()); |
432 ASSERT_NE(nullptr, first_selectable); | 307 ASSERT_NE(nullptr, first_selectable); |
433 EXPECT_EQ(1, first_selectable->GetCommand()); | 308 EXPECT_EQ(1, first_selectable->GetCommand()); |
434 // The last selectable item should be item "Two". | 309 // The last selectable item should be item "Two". |
435 last_selectable = FindInitialSelectableMenuItemUp(menu_item.get()); | 310 last_selectable = FindInitialSelectableMenuItemUp(menu_item()); |
436 ASSERT_NE(nullptr, last_selectable); | 311 ASSERT_NE(nullptr, last_selectable); |
437 EXPECT_EQ(2, last_selectable->GetCommand()); | 312 EXPECT_EQ(2, last_selectable->GetCommand()); |
438 | 313 |
439 // Leave only a single item "One" enabled. | 314 // Leave only a single item "One" enabled. |
440 menu_item->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(true); | 315 menu_item()->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(true); |
441 menu_item->GetSubmenu()->GetMenuItemAt(1)->SetEnabled(false); | 316 menu_item()->GetSubmenu()->GetMenuItemAt(1)->SetEnabled(false); |
442 menu_item->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); | 317 menu_item()->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); |
318 menu_item()->GetSubmenu()->GetMenuItemAt(3)->SetEnabled(false); | |
443 // The first selectable item should be item "One". | 319 // The first selectable item should be item "One". |
444 first_selectable = FindInitialSelectableMenuItemDown(menu_item.get()); | 320 first_selectable = FindInitialSelectableMenuItemDown(menu_item()); |
445 ASSERT_NE(nullptr, first_selectable); | 321 ASSERT_NE(nullptr, first_selectable); |
446 EXPECT_EQ(1, first_selectable->GetCommand()); | 322 EXPECT_EQ(1, first_selectable->GetCommand()); |
447 // The last selectable item should be item "One". | 323 // The last selectable item should be item "One". |
448 last_selectable = FindInitialSelectableMenuItemUp(menu_item.get()); | 324 last_selectable = FindInitialSelectableMenuItemUp(menu_item()); |
449 ASSERT_NE(nullptr, last_selectable); | 325 ASSERT_NE(nullptr, last_selectable); |
450 EXPECT_EQ(1, last_selectable->GetCommand()); | 326 EXPECT_EQ(1, last_selectable->GetCommand()); |
451 | 327 |
452 // Leave only a single item "Three" enabled. | 328 // Leave only a single item "Three" enabled. |
453 menu_item->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false); | 329 menu_item()->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false); |
454 menu_item->GetSubmenu()->GetMenuItemAt(1)->SetEnabled(false); | 330 menu_item()->GetSubmenu()->GetMenuItemAt(1)->SetEnabled(false); |
455 menu_item->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(true); | 331 menu_item()->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(true); |
332 menu_item()->GetSubmenu()->GetMenuItemAt(3)->SetEnabled(false); | |
456 // The first selectable item should be item "Three". | 333 // The first selectable item should be item "Three". |
457 first_selectable = FindInitialSelectableMenuItemDown(menu_item.get()); | 334 first_selectable = FindInitialSelectableMenuItemDown(menu_item()); |
458 ASSERT_NE(nullptr, first_selectable); | 335 ASSERT_NE(nullptr, first_selectable); |
459 EXPECT_EQ(3, first_selectable->GetCommand()); | 336 EXPECT_EQ(3, first_selectable->GetCommand()); |
460 // The last selectable item should be item "Three". | 337 // The last selectable item should be item "Three". |
461 last_selectable = FindInitialSelectableMenuItemUp(menu_item.get()); | 338 last_selectable = FindInitialSelectableMenuItemUp(menu_item()); |
462 ASSERT_NE(nullptr, last_selectable); | 339 ASSERT_NE(nullptr, last_selectable); |
463 EXPECT_EQ(3, last_selectable->GetCommand()); | 340 EXPECT_EQ(3, last_selectable->GetCommand()); |
464 | 341 |
465 // Leave only a single item ("Two") selected. It should be the first and the | 342 // Leave only a single item ("Two") selected. It should be the first and the |
466 // last selectable item. | 343 // last selectable item. |
467 menu_item->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false); | 344 menu_item()->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false); |
468 menu_item->GetSubmenu()->GetMenuItemAt(1)->SetEnabled(true); | 345 menu_item()->GetSubmenu()->GetMenuItemAt(1)->SetEnabled(true); |
469 menu_item->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); | 346 menu_item()->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); |
470 first_selectable = FindInitialSelectableMenuItemDown(menu_item.get()); | 347 menu_item()->GetSubmenu()->GetMenuItemAt(3)->SetEnabled(false); |
348 first_selectable = FindInitialSelectableMenuItemDown(menu_item()); | |
471 ASSERT_NE(nullptr, first_selectable); | 349 ASSERT_NE(nullptr, first_selectable); |
472 EXPECT_EQ(2, first_selectable->GetCommand()); | 350 EXPECT_EQ(2, first_selectable->GetCommand()); |
473 last_selectable = FindInitialSelectableMenuItemUp(menu_item.get()); | 351 last_selectable = FindInitialSelectableMenuItemUp(menu_item()); |
474 ASSERT_NE(nullptr, last_selectable); | 352 ASSERT_NE(nullptr, last_selectable); |
475 EXPECT_EQ(2, last_selectable->GetCommand()); | 353 EXPECT_EQ(2, last_selectable->GetCommand()); |
476 | 354 |
477 // There should be no next or previous selectable item since there is only a | 355 // There should be no next or previous selectable item since there is only a |
478 // single enabled item in the menu. | 356 // single enabled item in the menu. |
479 EXPECT_EQ(nullptr, FindNextSelectableMenuItem(menu_item.get(), 1)); | 357 EXPECT_EQ(nullptr, FindNextSelectableMenuItem(menu_item(), 1)); |
480 EXPECT_EQ(nullptr, FindPreviousSelectableMenuItem(menu_item.get(), 1)); | 358 EXPECT_EQ(nullptr, FindPreviousSelectableMenuItem(menu_item(), 1)); |
481 | 359 |
482 // Clear references in menu controller to the menu item that is going away. | 360 // Clear references in menu controller to the menu item that is going away. |
483 ResetSelection(); | 361 ResetSelection(); |
484 } | 362 } |
485 | 363 |
486 // Tests that opening menu and pressing 'Down' and 'Up' iterates over enabled | 364 // Tests that opening menu and pressing 'Down' and 'Up' iterates over enabled |
487 // items. | 365 // items. |
488 TEST_F(MenuControllerTest, NextSelectedItem) { | 366 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. | 367 // Disabling the item "Three" gets it skipped when using keyboard to navigate. |
496 menu_item->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); | 368 menu_item()->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); |
497 | |
498 SetupMenu(owner.get(), menu_item.get()); | |
499 | 369 |
500 // Fake initial hot selection. | 370 // Fake initial hot selection. |
501 SetPendingStateItem(menu_item->GetSubmenu()->GetMenuItemAt(0)); | 371 SetPendingStateItem(menu_item()->GetSubmenu()->GetMenuItemAt(0)); |
502 EXPECT_EQ(1, pending_state_item()->GetCommand()); | 372 EXPECT_EQ(1, pending_state_item()->GetCommand()); |
503 | 373 |
504 // Move down in the menu. | 374 // Move down in the menu. |
505 // Select next item. | 375 // Select next item. |
506 IncrementSelection(); | 376 IncrementSelection(); |
507 EXPECT_EQ(2, pending_state_item()->GetCommand()); | 377 EXPECT_EQ(2, pending_state_item()->GetCommand()); |
508 | 378 |
509 // Skip disabled item. | 379 // Skip disabled item. |
510 IncrementSelection(); | 380 IncrementSelection(); |
511 EXPECT_EQ(4, pending_state_item()->GetCommand()); | 381 EXPECT_EQ(4, pending_state_item()->GetCommand()); |
(...skipping 14 matching lines...) Expand all Loading... | |
526 // Select previous item. | 396 // Select previous item. |
527 DecrementSelection(); | 397 DecrementSelection(); |
528 EXPECT_EQ(1, pending_state_item()->GetCommand()); | 398 EXPECT_EQ(1, pending_state_item()->GetCommand()); |
529 | 399 |
530 // Clear references in menu controller to the menu item that is going away. | 400 // Clear references in menu controller to the menu item that is going away. |
531 ResetSelection(); | 401 ResetSelection(); |
532 } | 402 } |
533 | 403 |
534 // Tests that opening menu and pressing 'Up' selects the last enabled menu item. | 404 // Tests that opening menu and pressing 'Up' selects the last enabled menu item. |
535 TEST_F(MenuControllerTest, PreviousSelectedItem) { | 405 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. | 406 // Disabling the item "Four" gets it skipped when using keyboard to navigate. |
543 menu_item->GetSubmenu()->GetMenuItemAt(3)->SetEnabled(false); | 407 menu_item()->GetSubmenu()->GetMenuItemAt(3)->SetEnabled(false); |
544 | |
545 SetupMenu(owner.get(), menu_item.get()); | |
546 | 408 |
547 // Fake initial root item selection and submenu showing. | 409 // Fake initial root item selection and submenu showing. |
548 SetPendingStateItem(menu_item.get()); | 410 SetPendingStateItem(menu_item()); |
549 EXPECT_EQ(0, pending_state_item()->GetCommand()); | 411 EXPECT_EQ(0, pending_state_item()->GetCommand()); |
550 | 412 |
551 // Move up and select a previous (in our case the last enabled) item. | 413 // Move up and select a previous (in our case the last enabled) item. |
552 DecrementSelection(); | 414 DecrementSelection(); |
553 EXPECT_EQ(3, pending_state_item()->GetCommand()); | 415 EXPECT_EQ(3, pending_state_item()->GetCommand()); |
554 | 416 |
555 // Clear references in menu controller to the menu item that is going away. | 417 // Clear references in menu controller to the menu item that is going away. |
556 ResetSelection(); | 418 ResetSelection(); |
557 } | 419 } |
558 | 420 |
559 } // namespace test | 421 } // namespace test |
560 } // namespace views | 422 } // namespace views |
OLD | NEW |