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

Side by Side Diff: ui/views/controls/menu/menu_runner_cocoa_unittest.mm

Issue 2790773002: Cleanup MenuRunner API (Closed)
Patch Set: Rebase Created 3 years, 8 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
« no previous file with comments | « ui/views/controls/menu/menu_runner.cc ('k') | ui/views/controls/menu/menu_runner_handler.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 #import "ui/views/controls/menu/menu_runner_impl_cocoa.h" 5 #import "ui/views/controls/menu/menu_runner_impl_cocoa.h"
6 6
7 #import <Cocoa/Cocoa.h> 7 #import <Cocoa/Cocoa.h>
8 8
9 #include "base/macros.h" 9 #include "base/macros.h"
10 #include "base/run_loop.h" 10 #include "base/run_loop.h"
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
107 runner_ = NULL; 107 runner_ = NULL;
108 } 108 }
109 109
110 parent_->CloseNow(); 110 parent_->CloseNow();
111 ViewsTestBase::TearDown(); 111 ViewsTestBase::TearDown();
112 } 112 }
113 113
114 int IsAsync() const { return GetParam() == MenuType::VIEWS; } 114 int IsAsync() const { return GetParam() == MenuType::VIEWS; }
115 115
116 // Runs the menu after registering |callback| as the menu open callback. 116 // Runs the menu after registering |callback| as the menu open callback.
117 MenuRunner::RunResult RunMenu(const base::Closure& callback) { 117 void RunMenu(const base::Closure& callback) {
118 if (IsAsync()) { 118 if (IsAsync()) {
119 // Cancelling an async menu under MenuController::OpenMenuImpl() (which 119 // Cancelling an async menu under MenuController::OpenMenuImpl() (which
120 // invokes WillShowMenu()) will cause a UAF when that same function tries 120 // invokes WillShowMenu()) will cause a UAF when that same function tries
121 // to show the menu. So post a task instead. 121 // to show the menu. So post a task instead.
122 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); 122 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
123 } else { 123 } else {
124 menu_->set_menu_open_callback( 124 menu_->set_menu_open_callback(
125 base::Bind(&MenuRunnerCocoaTest::RunMenuWrapperCallback, 125 base::Bind(&MenuRunnerCocoaTest::RunMenuWrapperCallback,
126 base::Unretained(this), callback)); 126 base::Unretained(this), callback));
127 } 127 }
128 128
129 // Always pass ASYNC, even though native menus will be sync. 129 runner_->RunMenuAt(parent_, nullptr, gfx::Rect(), MENU_ANCHOR_TOPLEFT,
130 int run_types = MenuRunner::CONTEXT_MENU | MenuRunner::ASYNC; 130 MenuRunner::CONTEXT_MENU);
131 return MaybeRunAsync(runner_->RunMenuAt(parent_, nullptr, gfx::Rect(), 131 MaybeRunAsync();
132 MENU_ANCHOR_TOPLEFT, run_types));
133 } 132 }
134 133
135 // Runs then cancels a combobox menu and captures the frame of the anchoring 134 // Runs then cancels a combobox menu and captures the frame of the anchoring
136 // view. 135 // view.
137 MenuRunner::RunResult RunMenuAt(const gfx::Rect& anchor) { 136 void RunMenuAt(const gfx::Rect& anchor) {
138 last_anchor_frame_ = NSZeroRect; 137 last_anchor_frame_ = NSZeroRect;
139 138
140 // Should be one child (the compositor layer) before showing, and it should 139 // Should be one child (the compositor layer) before showing, and it should
141 // go up by one (the anchor view) while the menu is shown. 140 // go up by one (the anchor view) while the menu is shown.
142 EXPECT_EQ(1u, [[parent_->GetNativeView() subviews] count]); 141 EXPECT_EQ(1u, [[parent_->GetNativeView() subviews] count]);
143 142
144 base::Closure callback = 143 base::Closure callback =
145 base::Bind(&MenuRunnerCocoaTest::ComboboxRunMenuAtCallback, 144 base::Bind(&MenuRunnerCocoaTest::ComboboxRunMenuAtCallback,
146 base::Unretained(this)); 145 base::Unretained(this));
147 if (IsAsync()) 146 if (IsAsync())
148 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); 147 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
149 else 148 else
150 menu_->set_menu_open_callback(callback); 149 menu_->set_menu_open_callback(callback);
151 150
152 MenuRunner::RunResult result = MaybeRunAsync( 151 runner_->RunMenuAt(parent_, nullptr, anchor, MENU_ANCHOR_TOPLEFT,
153 runner_->RunMenuAt(parent_, nullptr, anchor, MENU_ANCHOR_TOPLEFT, 152 MenuRunner::COMBOBOX);
154 MenuRunner::COMBOBOX | MenuRunner::ASYNC)); 153 MaybeRunAsync();
155 154
156 // Ensure the anchor view is removed. 155 // Ensure the anchor view is removed.
157 EXPECT_EQ(1u, [[parent_->GetNativeView() subviews] count]); 156 EXPECT_EQ(1u, [[parent_->GetNativeView() subviews] count]);
158 return result;
159 } 157 }
160 158
161 void MenuCancelCallback() { 159 void MenuCancelCallback() {
162 runner_->Cancel(); 160 runner_->Cancel();
163 if (IsAsync()) { 161 if (IsAsync()) {
164 // Async menus report their cancellation immediately. 162 // Async menus report their cancellation immediately.
165 EXPECT_FALSE(runner_->IsRunning()); 163 EXPECT_FALSE(runner_->IsRunning());
166 } else { 164 } else {
167 // For a synchronous menu, MenuRunner::IsRunning() should return true 165 // For a synchronous menu, MenuRunner::IsRunning() should return true
168 // immediately after MenuRunner::Cancel() since the menu message loop has 166 // immediately after MenuRunner::Cancel() since the menu message loop has
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
206 ASSERT_EQ(2u, [subviews count]); 204 ASSERT_EQ(2u, [subviews count]);
207 last_anchor_frame_ = [[subviews objectAtIndex:1] frame]; 205 last_anchor_frame_ = [[subviews objectAtIndex:1] frame];
208 } else { 206 } else {
209 EXPECT_EQ(1u, [subviews count]); 207 EXPECT_EQ(1u, [subviews count]);
210 } 208 }
211 runner_->Cancel(); 209 runner_->Cancel();
212 } 210 }
213 211
214 // Run a nested message loop so that async and sync menus can be tested the 212 // Run a nested message loop so that async and sync menus can be tested the
215 // same way. 213 // same way.
216 MenuRunner::RunResult MaybeRunAsync(MenuRunner::RunResult run_result) { 214 void MaybeRunAsync() {
217 if (!IsAsync()) 215 if (!IsAsync())
218 return run_result; 216 return;
219 217
220 // Async menus should always return NORMAL_EXIT.
221 EXPECT_EQ(MenuRunner::NORMAL_EXIT, run_result);
222 base::RunLoop run_loop; 218 base::RunLoop run_loop;
223 quit_closure_ = run_loop.QuitClosure(); 219 quit_closure_ = run_loop.QuitClosure();
224 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( 220 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
225 FROM_HERE, quit_closure_, TestTimeouts::action_timeout()); 221 FROM_HERE, quit_closure_, TestTimeouts::action_timeout());
226 run_loop.Run(); 222 run_loop.Run();
227 223
228 // |quit_closure_| should be run by QuitAsyncRunLoop(), not the timeout. 224 // |quit_closure_| should be run by QuitAsyncRunLoop(), not the timeout.
229 EXPECT_TRUE(quit_closure_.is_null()); 225 EXPECT_TRUE(quit_closure_.is_null());
230 return run_result;
231 } 226 }
232 227
233 void QuitAsyncRunLoop() { 228 void QuitAsyncRunLoop() {
234 if (!IsAsync()) { 229 if (!IsAsync()) {
235 EXPECT_TRUE(quit_closure_.is_null()); 230 EXPECT_TRUE(quit_closure_.is_null());
236 return; 231 return;
237 } 232 }
238 ASSERT_FALSE(quit_closure_.is_null()); 233 ASSERT_FALSE(quit_closure_.is_null());
239 quit_closure_.Run(); 234 quit_closure_.Run();
240 quit_closure_.Reset(); 235 quit_closure_.Reset();
241 } 236 }
242 237
243 void MenuCloseCallback() { 238 void MenuCloseCallback() {
244 ++menu_close_count_; 239 ++menu_close_count_;
245 QuitAsyncRunLoop(); 240 QuitAsyncRunLoop();
246 } 241 }
247 242
248 base::Closure quit_closure_; 243 base::Closure quit_closure_;
249 244
250 DISALLOW_COPY_AND_ASSIGN(MenuRunnerCocoaTest); 245 DISALLOW_COPY_AND_ASSIGN(MenuRunnerCocoaTest);
251 }; 246 };
252 247
253 TEST_P(MenuRunnerCocoaTest, RunMenuAndCancel) { 248 TEST_P(MenuRunnerCocoaTest, RunMenuAndCancel) {
254 base::TimeTicks min_time = ui::EventTimeForNow(); 249 base::TimeTicks min_time = ui::EventTimeForNow();
255 250
256 MenuRunner::RunResult result = RunMenu(base::Bind( 251 RunMenu(base::Bind(&MenuRunnerCocoaTest::MenuCancelCallback,
257 &MenuRunnerCocoaTest::MenuCancelCallback, base::Unretained(this))); 252 base::Unretained(this)));
258 253
259 EXPECT_EQ(1, menu_close_count_); 254 EXPECT_EQ(1, menu_close_count_);
260 EXPECT_EQ(MenuRunner::NORMAL_EXIT, result);
261 EXPECT_FALSE(runner_->IsRunning()); 255 EXPECT_FALSE(runner_->IsRunning());
262 256
263 if (GetParam() == MenuType::VIEWS) { 257 if (GetParam() == MenuType::VIEWS) {
264 // MenuItemView's MenuRunnerImpl gets the closing time from MenuController:: 258 // MenuItemView's MenuRunnerImpl gets the closing time from MenuController::
265 // closing_event_time(). This is is reset on show, but only updated when an 259 // closing_event_time(). This is is reset on show, but only updated when an
266 // event closes the menu -- not a cancellation. 260 // event closes the menu -- not a cancellation.
267 EXPECT_EQ(runner_->GetClosingEventTime(), base::TimeTicks()); 261 EXPECT_EQ(runner_->GetClosingEventTime(), base::TimeTicks());
268 } else { 262 } else {
269 EXPECT_GE(runner_->GetClosingEventTime(), min_time); 263 EXPECT_GE(runner_->GetClosingEventTime(), min_time);
270 } 264 }
271 EXPECT_LE(runner_->GetClosingEventTime(), ui::EventTimeForNow()); 265 EXPECT_LE(runner_->GetClosingEventTime(), ui::EventTimeForNow());
272 266
273 // Cancel again. 267 // Cancel again.
274 runner_->Cancel(); 268 runner_->Cancel();
275 EXPECT_FALSE(runner_->IsRunning()); 269 EXPECT_FALSE(runner_->IsRunning());
276 EXPECT_EQ(1, menu_close_count_); 270 EXPECT_EQ(1, menu_close_count_);
277 } 271 }
278 272
279 TEST_P(MenuRunnerCocoaTest, RunMenuAndDelete) { 273 TEST_P(MenuRunnerCocoaTest, RunMenuAndDelete) {
280 MenuRunner::RunResult result = RunMenu(base::Bind( 274 RunMenu(base::Bind(&MenuRunnerCocoaTest::MenuDeleteCallback,
281 &MenuRunnerCocoaTest::MenuDeleteCallback, base::Unretained(this))); 275 base::Unretained(this)));
282 // Note the close callback is NOT invoked for deleted menus. 276 // Note the close callback is NOT invoked for deleted menus.
283 EXPECT_EQ(0, menu_close_count_); 277 EXPECT_EQ(0, menu_close_count_);
284
285 // Async menus always return NORMAL from RunMenuAt().
286 if (GetParam() == MenuType::VIEWS)
287 EXPECT_EQ(MenuRunner::NORMAL_EXIT, result);
288 else
289 EXPECT_EQ(MenuRunner::MENU_DELETED, result);
290 } 278 }
291 279
292 // Ensure a menu can be safely released immediately after a call to Cancel() in 280 // Ensure a menu can be safely released immediately after a call to Cancel() in
293 // the same run loop iteration. 281 // the same run loop iteration.
294 TEST_P(MenuRunnerCocoaTest, DestroyAfterCanceling) { 282 TEST_P(MenuRunnerCocoaTest, DestroyAfterCanceling) {
295 MenuRunner::RunResult result = 283 RunMenu(base::Bind(&MenuRunnerCocoaTest::MenuCancelAndDeleteCallback,
296 RunMenu(base::Bind(&MenuRunnerCocoaTest::MenuCancelAndDeleteCallback, 284 base::Unretained(this)));
297 base::Unretained(this)));
298 285
299 if (IsAsync()) { 286 if (IsAsync()) {
300 EXPECT_EQ(MenuRunner::NORMAL_EXIT, result);
301 EXPECT_EQ(1, menu_close_count_); 287 EXPECT_EQ(1, menu_close_count_);
302 } else { 288 } else {
303 EXPECT_EQ(MenuRunner::MENU_DELETED, result);
304 // For a synchronous menu, the deletion happens before the cancel can be 289 // For a synchronous menu, the deletion happens before the cancel can be
305 // processed, so the close callback will not be invoked. 290 // processed, so the close callback will not be invoked.
306 EXPECT_EQ(0, menu_close_count_); 291 EXPECT_EQ(0, menu_close_count_);
307 } 292 }
308 } 293 }
309 294
310 TEST_P(MenuRunnerCocoaTest, RunMenuTwice) { 295 TEST_P(MenuRunnerCocoaTest, RunMenuTwice) {
311 for (int i = 0; i < 2; ++i) { 296 for (int i = 0; i < 2; ++i) {
312 MenuRunner::RunResult result = RunMenu(base::Bind( 297 RunMenu(base::Bind(&MenuRunnerCocoaTest::MenuCancelCallback,
313 &MenuRunnerCocoaTest::MenuCancelCallback, base::Unretained(this))); 298 base::Unretained(this)));
314 EXPECT_EQ(MenuRunner::NORMAL_EXIT, result);
315 EXPECT_FALSE(runner_->IsRunning()); 299 EXPECT_FALSE(runner_->IsRunning());
316 EXPECT_EQ(i + 1, menu_close_count_); 300 EXPECT_EQ(i + 1, menu_close_count_);
317 } 301 }
318 } 302 }
319 303
320 TEST_P(MenuRunnerCocoaTest, CancelWithoutRunning) { 304 TEST_P(MenuRunnerCocoaTest, CancelWithoutRunning) {
321 runner_->Cancel(); 305 runner_->Cancel();
322 EXPECT_FALSE(runner_->IsRunning()); 306 EXPECT_FALSE(runner_->IsRunning());
323 EXPECT_EQ(base::TimeTicks(), runner_->GetClosingEventTime()); 307 EXPECT_EQ(base::TimeTicks(), runner_->GetClosingEventTime());
324 EXPECT_EQ(0, menu_close_count_); 308 EXPECT_EQ(0, menu_close_count_);
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
378 EXPECT_EQ(combobox_rect.right(), last_anchor_frame_.origin.x); 362 EXPECT_EQ(combobox_rect.right(), last_anchor_frame_.origin.x);
379 } 363 }
380 364
381 INSTANTIATE_TEST_CASE_P(, 365 INSTANTIATE_TEST_CASE_P(,
382 MenuRunnerCocoaTest, 366 MenuRunnerCocoaTest,
383 ::testing::Values(MenuType::NATIVE, MenuType::VIEWS), 367 ::testing::Values(MenuType::NATIVE, MenuType::VIEWS),
384 &MenuTypeToString); 368 &MenuTypeToString);
385 369
386 } // namespace test 370 } // namespace test
387 } // namespace views 371 } // namespace views
OLDNEW
« no previous file with comments | « ui/views/controls/menu/menu_runner.cc ('k') | ui/views/controls/menu/menu_runner_handler.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698