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

Side by Side Diff: chrome/browser/ui/cocoa/extensions/browser_action_button_interactive_uitest.mm

Issue 2852233002: Mac[Views]: Make native menus more responsive by pumping private runloop modes. (Closed)
Patch Set: respond to comments Created 3 years, 7 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 | « base/message_loop/message_pump_mac.mm ('k') | ui/base/cocoa/menu_controller.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 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 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 "chrome/browser/ui/cocoa/extensions/browser_action_button.h" 5 #import "chrome/browser/ui/cocoa/extensions/browser_action_button.h"
6 6
7 #import <Cocoa/Cocoa.h> 7 #import <Cocoa/Cocoa.h>
8 8
9 #include <memory> 9 #include <memory>
10 10
(...skipping 19 matching lines...) Expand all
30 #include "chrome/browser/ui/global_error/global_error_service.h" 30 #include "chrome/browser/ui/global_error/global_error_service.h"
31 #include "chrome/browser/ui/global_error/global_error_service_factory.h" 31 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
32 #include "chrome/browser/ui/toolbar/component_toolbar_actions_factory.h" 32 #include "chrome/browser/ui/toolbar/component_toolbar_actions_factory.h"
33 #include "chrome/browser/ui/toolbar/media_router_action.h" 33 #include "chrome/browser/ui/toolbar/media_router_action.h"
34 #include "chrome/browser/ui/toolbar/toolbar_actions_bar.h" 34 #include "chrome/browser/ui/toolbar/toolbar_actions_bar.h"
35 #include "chrome/browser/ui/toolbar/toolbar_actions_model.h" 35 #include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
36 #include "chrome/test/base/interactive_test_utils.h" 36 #include "chrome/test/base/interactive_test_utils.h"
37 #include "ui/base/cocoa/cocoa_base_utils.h" 37 #include "ui/base/cocoa/cocoa_base_utils.h"
38 #import "ui/events/test/cocoa_test_event_utils.h" 38 #import "ui/events/test/cocoa_test_event_utils.h"
39 39
40 // A helper class to wait for a menu to open and close.
41 @interface MenuWatcher : NSObject
42 - (id)initWithController:(MenuController*)controller;
43 @property(nonatomic, assign) base::Closure openClosure;
44 @property(nonatomic, assign) base::Closure closeClosure;
45 @end
46
40 namespace { 47 namespace {
41 48
42 const int kMenuPadding = 26; 49 const int kMenuPadding = 26;
43 50
44 // A simple error class that has a menu item. 51 // A simple error class that has a menu item.
45 class MenuError : public GlobalError { 52 class MenuError : public GlobalError {
46 public: 53 public:
47 MenuError() {} 54 MenuError() {}
48 ~MenuError() override {} 55 ~MenuError() override {}
49 56
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
86 93
87 // Moves the mouse (synchronously) to the center of the given |view|. 94 // Moves the mouse (synchronously) to the center of the given |view|.
88 void MoveMouseToCenter(NSView* view) { 95 void MoveMouseToCenter(NSView* view) {
89 NSPoint centerPoint = GetCenterPoint(view); 96 NSPoint centerPoint = GetCenterPoint(view);
90 base::RunLoop runLoop; 97 base::RunLoop runLoop;
91 ui_controls::SendMouseMoveNotifyWhenDone( 98 ui_controls::SendMouseMoveNotifyWhenDone(
92 centerPoint.x, centerPoint.y, runLoop.QuitClosure()); 99 centerPoint.x, centerPoint.y, runLoop.QuitClosure());
93 runLoop.Run(); 100 runLoop.Run();
94 } 101 }
95 102
96 // Simulates a click on the action button in the overflow menu, and runs 103 // Simulates a right-click on the action button in the overflow menu.
97 // |closure| upon completion. 104 void DispatchClickOnOverflowedAction(BrowserActionButton* action_button) {
98 void ClickOnOverflowedAction(ToolbarController* toolbarController, 105 NSEvent* event = nil; // The event doesn't matter when sending directly.
99 const base::Closure& closure) { 106 [action_button rightMouseDown:event];
100 AppMenuController* appMenuController = [toolbarController appMenuController]; 107 }
108
109 // ui_controls:: methods don't play nice when there is an open menu (like the
110 // app menu). Instead, ClickOnOverflowedAction() simulates a right click by
111 // feeding the event directly to the button. In regular interaction, this is
112 // dispatched after menuDidClose:. To simulate that, start the menu closing
113 // (with no action), but invoke the action on the button directly when the close
114 // is observed.
115 void ClickOnOverflowedAction(
116 base::scoped_nsobject<MenuWatcher> app_menu_watcher,
117 AppMenuController* app_menu_controller) {
101 // The app menu should start as open (since that's where the overflowed 118 // The app menu should start as open (since that's where the overflowed
102 // actions are). 119 // actions are).
103 EXPECT_TRUE([appMenuController isMenuOpen]); 120 EXPECT_TRUE([app_menu_controller isMenuOpen]);
104 BrowserActionsController* overflowController =
105 [appMenuController browserActionsController];
106 121
107 ASSERT_TRUE(overflowController); 122 BrowserActionsController* overflow_controller =
108 BrowserActionButton* actionButton = [overflowController buttonWithIndex:0]; 123 [app_menu_controller browserActionsController];
124
125 ASSERT_TRUE(overflow_controller);
126 BrowserActionButton* action_button = [overflow_controller buttonWithIndex:0];
127
109 // The action should be attached to a superview. 128 // The action should be attached to a superview.
110 EXPECT_TRUE([actionButton superview]); 129 EXPECT_TRUE([action_button superview]);
111 130
112 // ui_controls:: methods don't play nice when there is an open menu (like the 131 base::Closure invoke_action = base::Bind(&DispatchClickOnOverflowedAction,
113 // app menu). Instead, simulate a right click by feeding the event directly to 132 base::Unretained(action_button));
114 // the button. 133 [app_menu_watcher setCloseClosure:invoke_action];
115 NSPoint centerPoint = GetCenterPoint(actionButton);
116 NSEvent* mouseEvent = cocoa_test_event_utils::RightMouseDownAtPointInWindow(
117 centerPoint, [actionButton window]);
118 [actionButton rightMouseDown:mouseEvent];
119 134
120 // This should close the app menu. 135 // Close the app menu.
121 EXPECT_FALSE([appMenuController isMenuOpen]); 136 [app_menu_controller cancel];
122 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, closure);
123 } 137 }
124 138
125 } // namespace 139 } // namespace
126 140
127 // A simple helper menu delegate that will keep track of if a menu is opened, 141 // A simple helper menu delegate that will keep track of if a menu is opened,
128 // and closes them immediately (which is useful because message loops with 142 // and closes them immediately (which is useful because message loops with
129 // menus open in Cocoa don't play nicely with testing). 143 // menus open in Cocoa don't play nicely with testing).
130 @interface MenuHelper : NSObject<NSMenuDelegate> { 144 @interface MenuHelper : NSObject<NSMenuDelegate> {
131 // Whether or not a menu has been opened. This can be reset so the helper can 145 // Whether or not a menu has been opened. This can be reset so the helper can
132 // be used multiple times. 146 // be used multiple times.
133 BOOL menuOpened_; 147 BOOL menuOpened_;
134 148
149 // The closure to run when the menu opens, if any.
150 base::Closure openClosure_;
151
135 // A function to be called to verify state while the menu is open. 152 // A function to be called to verify state while the menu is open.
136 base::Closure verify_; 153 base::Closure verify_;
137 } 154 }
138 155
139 // Bare-bones init.
140 - (id)init;
141
142 @property(nonatomic, assign) BOOL menuOpened; 156 @property(nonatomic, assign) BOOL menuOpened;
157 @property(nonatomic, assign) base::Closure openClosure;
143 @property(nonatomic, assign) base::Closure verify; 158 @property(nonatomic, assign) base::Closure verify;
144 159
145 @end 160 @end
146 161
147 @implementation MenuHelper 162 @implementation MenuHelper
148 163
149 - (void)menuWillOpen:(NSMenu*)menu { 164 - (void)menuWillOpen:(NSMenu*)menu {
150 menuOpened_ = YES; 165 menuOpened_ = YES;
166 if (!openClosure_.is_null())
167 openClosure_.Run();
151 if (!verify_.is_null()) 168 if (!verify_.is_null())
152 verify_.Run(); 169 verify_.Run();
153 [menu cancelTracking]; 170 [menu cancelTracking];
154 } 171 }
155 172
156 - (id)init {
157 self = [super init];
158 return self;
159 }
160
161 @synthesize menuOpened = menuOpened_; 173 @synthesize menuOpened = menuOpened_;
174 @synthesize openClosure = openClosure_;
162 @synthesize verify = verify_; 175 @synthesize verify = verify_;
163 176
164 @end 177 @end
165 178
166 // A helper class to wait for a menu to open and close. 179 @implementation MenuWatcher {
167 @interface MenuWatcher : NSObject {
168 // The MenuController for the menu this object is watching. 180 // The MenuController for the menu this object is watching.
169 MenuController* menuController_; 181 MenuController* menuController_;
170 182
171 // The closure to run when the menu opens, if any. 183 // The closure to run when the menu opens, if any.
172 base::Closure openClosure_; 184 base::Closure openClosure_;
173 185
174 // The closure to run when the menu closes, if any. 186 // The closure to run when the menu closes, if any.
175 base::Closure closeClosure_; 187 base::Closure closeClosure_;
176 } 188 }
177 189
178 - (id)initWithController:(MenuController*)controller;
179
180 // Notifications from the MenuController.
181 - (void)menuDidClose:(NSNotification*)notification;
182 - (void)menuDidOpen:(NSNotification*)notification;
183
184 @property(nonatomic, assign) base::Closure openClosure;
185 @property(nonatomic, assign) base::Closure closeClosure;
186
187 @end
188
189 @implementation MenuWatcher
190
191 @synthesize openClosure = openClosure_; 190 @synthesize openClosure = openClosure_;
192 @synthesize closeClosure = closeClosure_; 191 @synthesize closeClosure = closeClosure_;
193 192
194 - (id)initWithController:(MenuController*)controller { 193 - (id)initWithController:(MenuController*)controller {
195 if (self = [super init]) { 194 if (self = [super init]) {
196 menuController_ = controller; 195 menuController_ = controller;
197 [[NSNotificationCenter defaultCenter] 196 [[NSNotificationCenter defaultCenter]
198 addObserver:self 197 addObserver:self
199 selector:@selector(menuDidOpen:) 198 selector:@selector(menuDidOpen:)
200 name:kMenuControllerMenuWillOpenNotification 199 name:kMenuControllerMenuWillOpenNotification
201 object:menuController_]; 200 object:menuController_];
202 [[NSNotificationCenter defaultCenter] 201 [[NSNotificationCenter defaultCenter]
203 addObserver:self 202 addObserver:self
204 selector:@selector(menuDidClose:) 203 selector:@selector(menuDidClose:)
205 name:kMenuControllerMenuDidCloseNotification 204 name:kMenuControllerMenuDidCloseNotification
206 object:menuController_]; 205 object:menuController_];
207 } 206 }
208 return self; 207 return self;
209 } 208 }
210 209
211 - (void)dealloc { 210 - (void)dealloc {
212 [[NSNotificationCenter defaultCenter] removeObserver:self]; 211 [[NSNotificationCenter defaultCenter] removeObserver:self];
213 [super dealloc]; 212 [super dealloc];
214 } 213 }
215 214
216 - (void)menuDidClose:(NSNotification*)notification { 215 - (void)menuDidClose:(NSNotification*)notification {
217 if (!closeClosure_.is_null()) { 216 if (!closeClosure_.is_null()) {
218 base::ThreadTaskRunnerHandle::Get()->PostTask( 217 // Run |closeClosure_| synchronously since it may depend on objects that are
219 FROM_HERE, base::ResetAndReturn(&closeClosure_)); 218 // torn down once the menu is closed and execution has returned to the main
219 // run loop.
220 closeClosure_.Run();
221 closeClosure_.Reset();
220 } 222 }
221 } 223 }
222 224
223 - (void)menuDidOpen:(NSNotification*)notification { 225 - (void)menuDidOpen:(NSNotification*)notification {
224 if (!openClosure_.is_null()) { 226 if (!openClosure_.is_null()) {
225 base::ThreadTaskRunnerHandle::Get()->PostTask( 227 base::ThreadTaskRunnerHandle::Get()->PostTask(
226 FROM_HERE, base::ResetAndReturn(&openClosure_)); 228 FROM_HERE, base::ResetAndReturn(&openClosure_));
227 } 229 }
228 } 230 }
229 231
(...skipping 20 matching lines...) Expand all
250 ExtensionBrowserTest::SetUpCommandLine(command_line); 252 ExtensionBrowserTest::SetUpCommandLine(command_line);
251 ToolbarActionsBar::disable_animations_for_testing_ = true; 253 ToolbarActionsBar::disable_animations_for_testing_ = true;
252 } 254 }
253 255
254 void TearDownOnMainThread() override { 256 void TearDownOnMainThread() override {
255 ToolbarActionsBar::disable_animations_for_testing_ = false; 257 ToolbarActionsBar::disable_animations_for_testing_ = false;
256 ExtensionBrowserTest::TearDownOnMainThread(); 258 ExtensionBrowserTest::TearDownOnMainThread();
257 } 259 }
258 260
259 // Opens the app menu and the context menu of the overflowed action, and 261 // Opens the app menu and the context menu of the overflowed action, and
260 // checks that the menus get opened/closed properly. |menuHelper| must be set 262 // checks that the menus get opened/closed properly.
261 // as the delegate for the context menu. 263 void OpenAppMenuAndActionContextMenu(MenuHelper* context_menu_helper) {
262 void OpenAppMenuAndActionContextMenu(MenuHelper* menuHelper) {
263 // Move the mouse over the app menu button. 264 // Move the mouse over the app menu button.
264 MoveMouseToCenter(appMenuButton()); 265 MoveMouseToCenter(appMenuButton());
265 266
266 // No menu yet (on the browser action). 267 // No menu yet (on the browser action).
267 EXPECT_FALSE([menuHelper menuOpened]); 268 EXPECT_FALSE([context_menu_helper menuOpened]);
268 base::RunLoop runLoop; 269 base::RunLoop run_loop;
269 // Click on the app menu, and pass in a callback to continue the test in 270 base::scoped_nsobject<MenuWatcher> app_menu_watcher(
270 // ClickOnOverflowedAction. Due to the blocking nature of Cocoa menus, we
271 // can't test the menu synchronously here by quitting the run loop when it
272 // opens, and instead need to test through a callback.
273 base::scoped_nsobject<MenuWatcher> menuWatcher(
274 [[MenuWatcher alloc] initWithController:appMenuController()]); 271 [[MenuWatcher alloc] initWithController:appMenuController()]);
275 [menuWatcher 272
276 setOpenClosure:base::Bind(&ClickOnOverflowedAction, 273 base::Closure close_with_action =
277 base::Unretained(toolbarController()), 274 base::Bind(&ClickOnOverflowedAction, app_menu_watcher,
278 runLoop.QuitClosure())]; 275 base::Unretained(appMenuController()));
276 [app_menu_watcher setOpenClosure:close_with_action];
277
278 // Quit the RunLoop below when the context menu opens. This dictates that
279 // the MenuHelper's verify action has also run.
280 [context_menu_helper setOpenClosure:run_loop.QuitClosure()];
281
282 // Show the app menu.
279 ui_controls::SendMouseEvents(ui_controls::LEFT, 283 ui_controls::SendMouseEvents(ui_controls::LEFT,
280 ui_controls::DOWN | ui_controls::UP); 284 ui_controls::DOWN | ui_controls::UP);
281 runLoop.Run(); 285 run_loop.Run();
282 286
283 // The menu opened on the main bar's action button rather than the 287 // The menu opened on the main bar's action button rather than the
284 // overflow's since Cocoa does not support running a menu within a menu. 288 // overflow's since Cocoa does not support running a menu within a menu.
285 EXPECT_TRUE([menuHelper menuOpened]); 289 EXPECT_TRUE([context_menu_helper menuOpened]);
286 } 290 }
287 291
288 ToolbarController* toolbarController() { return toolbarController_; } 292 ToolbarController* toolbarController() { return toolbarController_; }
289 AppMenuController* appMenuController() { return appMenuController_; } 293 AppMenuController* appMenuController() { return appMenuController_; }
290 ToolbarActionsModel* model() { return model_; } 294 ToolbarActionsModel* model() { return model_; }
291 NSView* appMenuButton() { return [toolbarController_ appMenuButton]; } 295 NSView* appMenuButton() { return [toolbarController_ appMenuButton]; }
292 296
293 private: 297 private:
294 ToolbarController* toolbarController_ = nil; 298 ToolbarController* toolbarController_ = nil;
295 AppMenuController* appMenuController_ = nil; 299 AppMenuController* appMenuController_ = nil;
(...skipping 375 matching lines...) Expand 10 before | Expand all | Expand 10 after
671 openAndCloseAppMenu(appMenuController()); 675 openAndCloseAppMenu(appMenuController());
672 676
673 // Move the extension back to the main bar, so an overflow bar is no longer 677 // Move the extension back to the main bar, so an overflow bar is no longer
674 // needed. Then open and close the app menu a couple times. 678 // needed. Then open and close the app menu a couple times.
675 // This tests that the menu properly cleans up after itself when an overflow 679 // This tests that the menu properly cleans up after itself when an overflow
676 // was present, and is no longer (fix for crbug.com/603241). 680 // was present, and is no longer (fix for crbug.com/603241).
677 model()->SetVisibleIconCount(1); 681 model()->SetVisibleIconCount(1);
678 openAndCloseAppMenu(appMenuController()); 682 openAndCloseAppMenu(appMenuController());
679 openAndCloseAppMenu(appMenuController()); 683 openAndCloseAppMenu(appMenuController());
680 } 684 }
OLDNEW
« no previous file with comments | « base/message_loop/message_pump_mac.mm ('k') | ui/base/cocoa/menu_controller.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698