Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(126)

Side by Side Diff: ui/views/controls/menu/menu_controller_unittest.cc

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

Powered by Google App Engine
This is Rietveld 408576698