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

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

Issue 2394123002: Views: Expose an on_closed callback via the MenuRunner constructor. (Closed)
Patch Set: default arg Created 4 years, 2 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_impl.cc » ('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/strings/utf_string_conversions.h" 11 #include "base/strings/utf_string_conversions.h"
12 #include "base/test/test_timeouts.h"
13 #include "base/threading/thread_task_runner_handle.h"
11 #import "testing/gtest_mac.h" 14 #import "testing/gtest_mac.h"
12 #include "ui/base/models/simple_menu_model.h" 15 #include "ui/base/models/simple_menu_model.h"
13 #include "ui/events/event_utils.h" 16 #include "ui/events/event_utils.h"
17 #include "ui/views/controls/menu/menu_runner_impl_adapter.h"
14 #include "ui/views/test/views_test_base.h" 18 #include "ui/views/test/views_test_base.h"
15 19
16 namespace views { 20 namespace views {
17 namespace test { 21 namespace test {
18 namespace { 22 namespace {
19 23
20 class TestModel : public ui::SimpleMenuModel { 24 class TestModel : public ui::SimpleMenuModel {
21 public: 25 public:
22 TestModel() : ui::SimpleMenuModel(&delegate_), delegate_(this) {} 26 TestModel() : ui::SimpleMenuModel(&delegate_), delegate_(this) {}
23 27
24 void set_checked_command(int command) { checked_command_ = command; } 28 void set_checked_command(int command) { checked_command_ = command; }
25 29
26 void set_menu_open_callback(const base::Closure& callback) { 30 void set_menu_open_callback(const base::Closure& callback) {
27 menu_open_callback_ = callback; 31 menu_open_callback_ = callback;
28 } 32 }
29 33
30 private: 34 private:
31 class Delegate : public ui::SimpleMenuModel::Delegate { 35 class Delegate : public ui::SimpleMenuModel::Delegate {
32 public: 36 public:
33 explicit Delegate(TestModel* model) : model_(model) {} 37 explicit Delegate(TestModel* model) : model_(model) {}
34 bool IsCommandIdChecked(int command_id) const override { 38 bool IsCommandIdChecked(int command_id) const override {
35 return command_id == model_->checked_command_; 39 return command_id == model_->checked_command_;
36 } 40 }
37 bool IsCommandIdEnabled(int command_id) const override { return true; } 41 bool IsCommandIdEnabled(int command_id) const override { return true; }
38 void ExecuteCommand(int command_id, int event_flags) override {} 42 void ExecuteCommand(int command_id, int event_flags) override {}
39 43
40 void MenuWillShow(SimpleMenuModel* source) override { 44 void MenuWillShow(SimpleMenuModel* source) override {
41 model_->menu_open_callback_.Run(); 45 if (!model_->menu_open_callback_.is_null())
46 model_->menu_open_callback_.Run();
42 } 47 }
43 48
44 private: 49 private:
45 TestModel* model_; 50 TestModel* model_;
46 51
47 DISALLOW_COPY_AND_ASSIGN(Delegate); 52 DISALLOW_COPY_AND_ASSIGN(Delegate);
48 }; 53 };
49 54
50 private: 55 private:
51 int checked_command_ = -1; 56 int checked_command_ = -1;
52 Delegate delegate_; 57 Delegate delegate_;
53 base::Closure menu_open_callback_; 58 base::Closure menu_open_callback_;
54 59
55 DISALLOW_COPY_AND_ASSIGN(TestModel); 60 DISALLOW_COPY_AND_ASSIGN(TestModel);
56 }; 61 };
57 62
63 enum class MenuType { NATIVE, VIEWS };
64
65 std::string MenuTypeToString(::testing::TestParamInfo<MenuType> info) {
66 return info.param == MenuType::VIEWS ? "VIEWS_MenuItemView" : "NATIVE_NSMenu";
67 }
68
58 } // namespace 69 } // namespace
59 70
60 class MenuRunnerCocoaTest : public ViewsTestBase { 71 class MenuRunnerCocoaTest : public ViewsTestBase,
72 public ::testing::WithParamInterface<MenuType> {
61 public: 73 public:
62 enum { 74 enum {
63 kWindowHeight = 200, 75 kWindowHeight = 200,
64 kWindowOffset = 100, 76 kWindowOffset = 100,
65 }; 77 };
66 78
67 MenuRunnerCocoaTest() {} 79 MenuRunnerCocoaTest() {}
68 ~MenuRunnerCocoaTest() override {} 80 ~MenuRunnerCocoaTest() override {}
69 81
70 void SetUp() override { 82 void SetUp() override {
71 const int kWindowWidth = 300; 83 const int kWindowWidth = 300;
72 ViewsTestBase::SetUp(); 84 ViewsTestBase::SetUp();
73 85
74 menu_.reset(new TestModel()); 86 menu_.reset(new TestModel());
75 menu_->AddCheckItem(0, base::ASCIIToUTF16("Menu Item")); 87 menu_->AddCheckItem(0, base::ASCIIToUTF16("Menu Item"));
76 88
77 parent_ = new views::Widget(); 89 parent_ = new views::Widget();
78 parent_->Init(CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS)); 90 parent_->Init(CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS));
79 parent_->SetBounds( 91 parent_->SetBounds(
80 gfx::Rect(kWindowOffset, kWindowOffset, kWindowWidth, kWindowHeight)); 92 gfx::Rect(kWindowOffset, kWindowOffset, kWindowWidth, kWindowHeight));
81 parent_->Show(); 93 parent_->Show();
82 94
83 runner_ = new internal::MenuRunnerImplCocoa(menu_.get()); 95 base::Closure on_close = base::Bind(&MenuRunnerCocoaTest::MenuCloseCallback,
96 base::Unretained(this));
97 if (GetParam() == MenuType::NATIVE)
98 runner_ = new internal::MenuRunnerImplCocoa(menu_.get(), on_close);
99 else
100 runner_ = new internal::MenuRunnerImplAdapter(menu_.get(), on_close);
84 EXPECT_FALSE(runner_->IsRunning()); 101 EXPECT_FALSE(runner_->IsRunning());
85 } 102 }
86 103
87 void TearDown() override { 104 void TearDown() override {
88 if (runner_) { 105 if (runner_) {
89 runner_->Release(); 106 runner_->Release();
90 runner_ = NULL; 107 runner_ = NULL;
91 } 108 }
92 109
93 parent_->CloseNow(); 110 parent_->CloseNow();
94 ViewsTestBase::TearDown(); 111 ViewsTestBase::TearDown();
95 } 112 }
96 113
114 int IsAsync() const { return GetParam() == MenuType::VIEWS; }
115
97 // Runs the menu after registering |callback| as the menu open callback. 116 // Runs the menu after registering |callback| as the menu open callback.
98 MenuRunner::RunResult RunMenu(const base::Closure& callback) { 117 MenuRunner::RunResult RunMenu(const base::Closure& callback) {
99 menu_->set_menu_open_callback( 118 if (IsAsync()) {
100 base::Bind(&MenuRunnerCocoaTest::RunMenuWrapperCallback, 119 // Cancelling an async menu under MenuController::OpenMenuImpl() (which
101 base::Unretained(this), callback)); 120 // invokes WillShowMenu()) will cause a UAF when that same function tries
102 return runner_->RunMenuAt(parent_, nullptr, gfx::Rect(), 121 // to show the menu. So post a task instead.
103 MENU_ANCHOR_TOPLEFT, MenuRunner::CONTEXT_MENU); 122 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
123 } else {
124 menu_->set_menu_open_callback(
125 base::Bind(&MenuRunnerCocoaTest::RunMenuWrapperCallback,
126 base::Unretained(this), callback));
127 }
128
129 // Always pass ASYNC, even though native menus will be sync.
130 int run_types = MenuRunner::CONTEXT_MENU | MenuRunner::ASYNC;
131 return MaybeRunAsync(runner_->RunMenuAt(parent_, nullptr, gfx::Rect(),
132 MENU_ANCHOR_TOPLEFT, run_types));
104 } 133 }
105 134
106 // Runs then cancels a combobox menu and captures the frame of the anchoring 135 // Runs then cancels a combobox menu and captures the frame of the anchoring
107 // view. 136 // view.
108 MenuRunner::RunResult RunMenuAt(const gfx::Rect& anchor) { 137 MenuRunner::RunResult RunMenuAt(const gfx::Rect& anchor) {
109 last_anchor_frame_ = NSZeroRect; 138 last_anchor_frame_ = NSZeroRect;
110 139
111 // Should be one child (the compositor layer) before showing, and it should 140 // Should be one child (the compositor layer) before showing, and it should
112 // go up by one (the anchor view) while the menu is shown. 141 // go up by one (the anchor view) while the menu is shown.
113 EXPECT_EQ(1u, [[parent_->GetNativeView() subviews] count]); 142 EXPECT_EQ(1u, [[parent_->GetNativeView() subviews] count]);
114 143
115 menu_->set_menu_open_callback(base::Bind( 144 base::Closure callback =
116 &MenuRunnerCocoaTest::RunMenuAtCallback, base::Unretained(this))); 145 base::Bind(&MenuRunnerCocoaTest::ComboboxRunMenuAtCallback,
146 base::Unretained(this));
147 if (IsAsync())
148 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
149 else
150 menu_->set_menu_open_callback(callback);
117 151
118 MenuRunner::RunResult result = runner_->RunMenuAt( 152 MenuRunner::RunResult result = MaybeRunAsync(
119 parent_, nullptr, anchor, MENU_ANCHOR_TOPLEFT, MenuRunner::COMBOBOX); 153 runner_->RunMenuAt(parent_, nullptr, anchor, MENU_ANCHOR_TOPLEFT,
154 MenuRunner::COMBOBOX | MenuRunner::ASYNC));
120 155
121 // Ensure the anchor view is removed. 156 // Ensure the anchor view is removed.
122 EXPECT_EQ(1u, [[parent_->GetNativeView() subviews] count]); 157 EXPECT_EQ(1u, [[parent_->GetNativeView() subviews] count]);
123 return result; 158 return result;
124 } 159 }
125 160
126 void MenuCancelCallback() { 161 void MenuCancelCallback() {
127 runner_->Cancel(); 162 runner_->Cancel();
128 // For a syncronous menu, MenuRunner::IsRunning() should return true 163 if (IsAsync()) {
129 // immediately after MenuRunner::Cancel() since the menu message loop has 164 // Async menus report their cancellation immediately.
130 // not yet terminated. It has only been marked for termination. 165 EXPECT_FALSE(runner_->IsRunning());
131 EXPECT_TRUE(runner_->IsRunning()); 166 } else {
167 // For a synchronous menu, MenuRunner::IsRunning() should return true
168 // immediately after MenuRunner::Cancel() since the menu message loop has
169 // not yet terminated. It has only been marked for termination.
170 EXPECT_TRUE(runner_->IsRunning());
171 }
132 } 172 }
133 173
134 void MenuDeleteCallback() { 174 void MenuDeleteCallback() {
135 runner_->Release(); 175 runner_->Release();
136 runner_ = nullptr; 176 runner_ = nullptr;
177 // Deleting an async menu intentionally does not invoke MenuCloseCallback().
178 // (The callback is typically a method on something in the process of being
179 // destroyed). So invoke QuitAsyncRunLoop() here as well.
180 QuitAsyncRunLoop();
137 } 181 }
138 182
139 void MenuCancelAndDeleteCallback() { 183 void MenuCancelAndDeleteCallback() {
140 runner_->Cancel(); 184 runner_->Cancel();
141 runner_->Release(); 185 runner_->Release();
142 runner_ = nullptr; 186 runner_ = nullptr;
143 } 187 }
144 188
145 protected: 189 protected:
146 std::unique_ptr<TestModel> menu_; 190 std::unique_ptr<TestModel> menu_;
147 internal::MenuRunnerImplCocoa* runner_ = nullptr; 191 internal::MenuRunnerImplInterface* runner_ = nullptr;
148 views::Widget* parent_ = nullptr; 192 views::Widget* parent_ = nullptr;
149 NSRect last_anchor_frame_ = NSZeroRect; 193 NSRect last_anchor_frame_ = NSZeroRect;
194 int menu_close_count_ = 0;
150 195
151 private: 196 private:
152 void RunMenuWrapperCallback(const base::Closure& callback) { 197 void RunMenuWrapperCallback(const base::Closure& callback) {
153 EXPECT_TRUE(runner_->IsRunning()); 198 EXPECT_TRUE(runner_->IsRunning());
154 callback.Run(); 199 callback.Run();
155 } 200 }
156 201
157 void RunMenuAtCallback() { 202 void ComboboxRunMenuAtCallback() {
158 NSArray* subviews = [parent_->GetNativeView() subviews]; 203 NSArray* subviews = [parent_->GetNativeView() subviews];
159 EXPECT_EQ(2u, [subviews count]); 204 // An anchor view should only be added for Native menus.
160 last_anchor_frame_ = [[subviews objectAtIndex:1] frame]; 205 if (GetParam() == MenuType::NATIVE) {
206 ASSERT_EQ(2u, [subviews count]);
207 last_anchor_frame_ = [[subviews objectAtIndex:1] frame];
208 } else {
209 EXPECT_EQ(1u, [subviews count]);
210 }
161 runner_->Cancel(); 211 runner_->Cancel();
162 } 212 }
163 213
214 // Run a nested message loop so that async and sync menus can be tested the
215 // same way.
216 MenuRunner::RunResult MaybeRunAsync(MenuRunner::RunResult run_result) {
217 if (!IsAsync())
218 return run_result;
219
220 // Async menus should always return NORMAL_EXIT.
221 EXPECT_EQ(MenuRunner::NORMAL_EXIT, run_result);
222 base::RunLoop run_loop;
223 quit_closure_ = run_loop.QuitClosure();
224 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
225 FROM_HERE, quit_closure_, TestTimeouts::action_timeout());
226 run_loop.Run();
227
228 // |quit_closure_| should be run by QuitAsyncRunLoop(), not the timeout.
229 EXPECT_TRUE(quit_closure_.is_null());
230 return run_result;
231 }
232
233 void QuitAsyncRunLoop() {
234 if (!IsAsync()) {
235 EXPECT_TRUE(quit_closure_.is_null());
236 return;
237 }
238 ASSERT_FALSE(quit_closure_.is_null());
239 quit_closure_.Run();
240 quit_closure_.Reset();
241 }
242
243 void MenuCloseCallback() {
244 ++menu_close_count_;
245 QuitAsyncRunLoop();
246 }
247
248 base::Closure quit_closure_;
249
164 DISALLOW_COPY_AND_ASSIGN(MenuRunnerCocoaTest); 250 DISALLOW_COPY_AND_ASSIGN(MenuRunnerCocoaTest);
165 }; 251 };
166 252
167 TEST_F(MenuRunnerCocoaTest, RunMenuAndCancel) { 253 TEST_P(MenuRunnerCocoaTest, RunMenuAndCancel) {
168 base::TimeTicks min_time = ui::EventTimeForNow(); 254 base::TimeTicks min_time = ui::EventTimeForNow();
169 255
170 MenuRunner::RunResult result = RunMenu(base::Bind( 256 MenuRunner::RunResult result = RunMenu(base::Bind(
171 &MenuRunnerCocoaTest::MenuCancelCallback, base::Unretained(this))); 257 &MenuRunnerCocoaTest::MenuCancelCallback, base::Unretained(this)));
172 258
259 EXPECT_EQ(1, menu_close_count_);
173 EXPECT_EQ(MenuRunner::NORMAL_EXIT, result); 260 EXPECT_EQ(MenuRunner::NORMAL_EXIT, result);
174 EXPECT_FALSE(runner_->IsRunning()); 261 EXPECT_FALSE(runner_->IsRunning());
175 262
176 EXPECT_GE(runner_->GetClosingEventTime(), min_time); 263 if (GetParam() == MenuType::VIEWS) {
264 // MenuItemView's MenuRunnerImpl gets the closing time from MenuController::
265 // closing_event_time(). This is is reset on show, but only updated when an
266 // event closes the menu -- not a cancellation.
267 EXPECT_EQ(runner_->GetClosingEventTime(), base::TimeTicks());
268 } else {
269 EXPECT_GE(runner_->GetClosingEventTime(), min_time);
270 }
177 EXPECT_LE(runner_->GetClosingEventTime(), ui::EventTimeForNow()); 271 EXPECT_LE(runner_->GetClosingEventTime(), ui::EventTimeForNow());
178 272
179 // Cancel again. 273 // Cancel again.
180 runner_->Cancel(); 274 runner_->Cancel();
181 EXPECT_FALSE(runner_->IsRunning()); 275 EXPECT_FALSE(runner_->IsRunning());
276 EXPECT_EQ(1, menu_close_count_);
182 } 277 }
183 278
184 TEST_F(MenuRunnerCocoaTest, RunMenuAndDelete) { 279 TEST_P(MenuRunnerCocoaTest, RunMenuAndDelete) {
185 MenuRunner::RunResult result = RunMenu(base::Bind( 280 MenuRunner::RunResult result = RunMenu(base::Bind(
186 &MenuRunnerCocoaTest::MenuDeleteCallback, base::Unretained(this))); 281 &MenuRunnerCocoaTest::MenuDeleteCallback, base::Unretained(this)));
187 EXPECT_EQ(MenuRunner::MENU_DELETED, result); 282 // Note the close callback is NOT invoked for deleted menus.
283 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);
188 } 290 }
189 291
190 // Ensure a menu can be safely released immediately after a call to Cancel() in 292 // Ensure a menu can be safely released immediately after a call to Cancel() in
191 // the same run loop iteration. 293 // the same run loop iteration.
192 TEST_F(MenuRunnerCocoaTest, DestroyAfterCanceling) { 294 TEST_P(MenuRunnerCocoaTest, DestroyAfterCanceling) {
193 MenuRunner::RunResult result = 295 MenuRunner::RunResult result =
194 RunMenu(base::Bind(&MenuRunnerCocoaTest::MenuCancelAndDeleteCallback, 296 RunMenu(base::Bind(&MenuRunnerCocoaTest::MenuCancelAndDeleteCallback,
195 base::Unretained(this))); 297 base::Unretained(this)));
196 EXPECT_EQ(MenuRunner::MENU_DELETED, result); 298
299 if (IsAsync()) {
300 EXPECT_EQ(MenuRunner::NORMAL_EXIT, result);
301 EXPECT_EQ(1, menu_close_count_);
302 } else {
303 EXPECT_EQ(MenuRunner::MENU_DELETED, result);
304 // For a synchronous menu, the deletion happens before the cancel can be
305 // processed, so the close callback will not be invoked.
306 EXPECT_EQ(0, menu_close_count_);
307 }
197 } 308 }
198 309
199 TEST_F(MenuRunnerCocoaTest, RunMenuTwice) { 310 TEST_P(MenuRunnerCocoaTest, RunMenuTwice) {
200 for (int i = 0; i < 2; ++i) { 311 for (int i = 0; i < 2; ++i) {
201 MenuRunner::RunResult result = RunMenu(base::Bind( 312 MenuRunner::RunResult result = RunMenu(base::Bind(
202 &MenuRunnerCocoaTest::MenuCancelCallback, base::Unretained(this))); 313 &MenuRunnerCocoaTest::MenuCancelCallback, base::Unretained(this)));
203 EXPECT_EQ(MenuRunner::NORMAL_EXIT, result); 314 EXPECT_EQ(MenuRunner::NORMAL_EXIT, result);
204 EXPECT_FALSE(runner_->IsRunning()); 315 EXPECT_FALSE(runner_->IsRunning());
316 EXPECT_EQ(i + 1, menu_close_count_);
205 } 317 }
206 } 318 }
207 319
208 TEST_F(MenuRunnerCocoaTest, CancelWithoutRunning) { 320 TEST_P(MenuRunnerCocoaTest, CancelWithoutRunning) {
209 runner_->Cancel(); 321 runner_->Cancel();
210 EXPECT_FALSE(runner_->IsRunning()); 322 EXPECT_FALSE(runner_->IsRunning());
211 EXPECT_EQ(base::TimeTicks(), runner_->GetClosingEventTime()); 323 EXPECT_EQ(base::TimeTicks(), runner_->GetClosingEventTime());
324 EXPECT_EQ(0, menu_close_count_);
212 } 325 }
213 326
214 TEST_F(MenuRunnerCocoaTest, DeleteWithoutRunning) { 327 TEST_P(MenuRunnerCocoaTest, DeleteWithoutRunning) {
215 runner_->Release(); 328 runner_->Release();
216 runner_ = NULL; 329 runner_ = NULL;
330 EXPECT_EQ(0, menu_close_count_);
217 } 331 }
218 332
219 // Tests anchoring of the menus used for toolkit-views Comboboxes. 333 // Tests anchoring of the menus used for toolkit-views Comboboxes.
220 TEST_F(MenuRunnerCocoaTest, ComboboxAnchoring) { 334 TEST_P(MenuRunnerCocoaTest, ComboboxAnchoring) {
221 // Combobox at 20,10 in the Widget. 335 // Combobox at 20,10 in the Widget.
222 const gfx::Rect combobox_rect(20, 10, 80, 50); 336 const gfx::Rect combobox_rect(20, 10, 80, 50);
223 337
224 // Menu anchor rects are always in screen coordinates. The window is frameless 338 // Menu anchor rects are always in screen coordinates. The window is frameless
225 // so offset by the bounds. 339 // so offset by the bounds.
226 gfx::Rect anchor_rect = combobox_rect; 340 gfx::Rect anchor_rect = combobox_rect;
227 anchor_rect.Offset(kWindowOffset, kWindowOffset); 341 anchor_rect.Offset(kWindowOffset, kWindowOffset);
228 RunMenuAt(anchor_rect); 342 RunMenuAt(anchor_rect);
229 343
344 if (GetParam() != MenuType::NATIVE) {
345 // Combobox anchoring is only implemented for native menus.
346 EXPECT_NSEQ(NSZeroRect, last_anchor_frame_);
347 return;
348 }
349
230 // Nothing is checked, so the anchor view should have no height, to ensure the 350 // Nothing is checked, so the anchor view should have no height, to ensure the
231 // menu goes below the anchor rect. There should also be no x-offset since the 351 // menu goes below the anchor rect. There should also be no x-offset since the
232 // there is no need to line-up text. 352 // there is no need to line-up text.
233 EXPECT_NSEQ( 353 EXPECT_NSEQ(
234 NSMakeRect(combobox_rect.x(), kWindowHeight - combobox_rect.bottom(), 354 NSMakeRect(combobox_rect.x(), kWindowHeight - combobox_rect.bottom(),
235 combobox_rect.width(), 0), 355 combobox_rect.width(), 0),
236 last_anchor_frame_); 356 last_anchor_frame_);
237 357
238 menu_->set_checked_command(0); 358 menu_->set_checked_command(0);
239 RunMenuAt(anchor_rect); 359 RunMenuAt(anchor_rect);
(...skipping 11 matching lines...) Expand all
251 EXPECT_NE(0, NSHeight(last_anchor_frame_)); 371 EXPECT_NE(0, NSHeight(last_anchor_frame_));
252 372
253 // In RTL, Cocoa messes up the positioning unless the anchor rectangle is 373 // In RTL, Cocoa messes up the positioning unless the anchor rectangle is
254 // offset to the right of the view. The offset for the checkmark is also 374 // offset to the right of the view. The offset for the checkmark is also
255 // skipped, to give a better match to native behavior. 375 // skipped, to give a better match to native behavior.
256 base::i18n::SetICUDefaultLocale("he"); 376 base::i18n::SetICUDefaultLocale("he");
257 RunMenuAt(anchor_rect); 377 RunMenuAt(anchor_rect);
258 EXPECT_EQ(combobox_rect.right(), last_anchor_frame_.origin.x); 378 EXPECT_EQ(combobox_rect.right(), last_anchor_frame_.origin.x);
259 } 379 }
260 380
381 INSTANTIATE_TEST_CASE_P(,
382 MenuRunnerCocoaTest,
383 ::testing::Values(MenuType::NATIVE, MenuType::VIEWS),
384 &MenuTypeToString);
385
261 } // namespace test 386 } // namespace test
262 } // namespace views 387 } // namespace views
OLDNEW
« no previous file with comments | « ui/views/controls/menu/menu_runner.cc ('k') | ui/views/controls/menu/menu_runner_impl.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698