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