| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 } |
| OLD | NEW |