1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#import "chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac.h" 6 7#import <Cocoa/Cocoa.h> 8 9#include "base/command_line.h" 10#include "base/mac/scoped_nsobject.h" 11#include "base/strings/sys_string_conversions.h" 12#include "chrome/browser/apps/app_browsertest_util.h" 13#include "chrome/browser/extensions/extension_service.h" 14#include "chrome/browser/profiles/profile.h" 15#include "chrome/browser/ui/browser_iterator.h" 16#include "chrome/browser/ui/browser_window.h" 17#include "chrome/common/chrome_switches.h" 18#include "extensions/browser/app_window/app_window_registry.h" 19#include "extensions/browser/app_window/native_app_window.h" 20#include "extensions/browser/uninstall_reason.h" 21#include "extensions/common/extension.h" 22#include "extensions/test/extension_test_message_listener.h" 23 24namespace { 25 26class AppShimMenuControllerBrowserTest 27 : public extensions::PlatformAppBrowserTest { 28 protected: 29 AppShimMenuControllerBrowserTest() 30 : app_1_(NULL), 31 app_2_(NULL), 32 initial_menu_item_count_(0) {} 33 34 virtual ~AppShimMenuControllerBrowserTest() {} 35 36 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 37 PlatformAppBrowserTest::SetUpCommandLine(command_line); 38 } 39 40 // Start two apps and wait for them to be launched. 41 void SetUpApps() { 42 ExtensionTestMessageListener listener_1("Launched", false); 43 app_1_ = InstallAndLaunchPlatformApp("minimal_id"); 44 ASSERT_TRUE(listener_1.WaitUntilSatisfied()); 45 ExtensionTestMessageListener listener_2("Launched", false); 46 app_2_ = InstallAndLaunchPlatformApp("minimal"); 47 ASSERT_TRUE(listener_2.WaitUntilSatisfied()); 48 49 initial_menu_item_count_ = [[[NSApp mainMenu] itemArray] count]; 50 } 51 52 void CheckHasAppMenus(const extensions::Extension* app) const { 53 const int kExtraTopLevelItems = 4; 54 NSArray* item_array = [[NSApp mainMenu] itemArray]; 55 EXPECT_EQ(initial_menu_item_count_ + kExtraTopLevelItems, 56 [item_array count]); 57 for (NSUInteger i = 0; i < initial_menu_item_count_; ++i) 58 EXPECT_TRUE([[item_array objectAtIndex:i] isHidden]); 59 NSMenuItem* app_menu = [item_array objectAtIndex:initial_menu_item_count_]; 60 EXPECT_EQ(app->id(), base::SysNSStringToUTF8([app_menu title])); 61 EXPECT_EQ(app->name(), 62 base::SysNSStringToUTF8([[app_menu submenu] title])); 63 for (NSUInteger i = initial_menu_item_count_; 64 i < initial_menu_item_count_ + kExtraTopLevelItems; 65 ++i) { 66 NSMenuItem* menu = [item_array objectAtIndex:i]; 67 EXPECT_GT([[menu submenu] numberOfItems], 0); 68 EXPECT_FALSE([menu isHidden]); 69 } 70 } 71 72 void CheckNoAppMenus() const { 73 NSArray* item_array = [[NSApp mainMenu] itemArray]; 74 EXPECT_EQ(initial_menu_item_count_, [item_array count]); 75 for (NSUInteger i = 0; i < initial_menu_item_count_; ++i) 76 EXPECT_FALSE([[item_array objectAtIndex:i] isHidden]); 77 } 78 79 const extensions::Extension* app_1_; 80 const extensions::Extension* app_2_; 81 NSUInteger initial_menu_item_count_; 82 83 private: 84 DISALLOW_COPY_AND_ASSIGN(AppShimMenuControllerBrowserTest); 85}; 86 87// Test that focusing an app window changes the menu bar. 88IN_PROC_BROWSER_TEST_F(AppShimMenuControllerBrowserTest, 89 PlatformAppFocusUpdatesMenuBar) { 90 SetUpApps(); 91 // When an app is focused, all Chrome menu items should be hidden, and a menu 92 // item for the app should be added. 93 extensions::AppWindow* app_1_app_window = 94 extensions::AppWindowRegistry::Get(profile()) 95 ->GetAppWindowsForApp(app_1_->id()).front(); 96 [[NSNotificationCenter defaultCenter] 97 postNotificationName:NSWindowDidBecomeMainNotification 98 object:app_1_app_window->GetNativeWindow()]; 99 CheckHasAppMenus(app_1_); 100 101 // When another app is focused, the menu item for the app should change. 102 extensions::AppWindow* app_2_app_window = 103 extensions::AppWindowRegistry::Get(profile()) 104 ->GetAppWindowsForApp(app_2_->id()).front(); 105 [[NSNotificationCenter defaultCenter] 106 postNotificationName:NSWindowDidBecomeMainNotification 107 object:app_2_app_window->GetNativeWindow()]; 108 CheckHasAppMenus(app_2_); 109 110 // When a browser window is focused, the menu items for the app should be 111 // removed. 112 BrowserWindow* chrome_window = chrome::BrowserIterator()->window(); 113 [[NSNotificationCenter defaultCenter] 114 postNotificationName:NSWindowDidBecomeMainNotification 115 object:chrome_window->GetNativeWindow()]; 116 CheckNoAppMenus(); 117 118 // When an app window is closed and there are no other app windows, the menu 119 // items for the app should be removed. 120 app_1_app_window->GetBaseWindow()->Close(); 121 chrome_window->Close(); 122 [[NSNotificationCenter defaultCenter] 123 postNotificationName:NSWindowWillCloseNotification 124 object:app_2_app_window->GetNativeWindow()]; 125 CheckNoAppMenus(); 126} 127 128IN_PROC_BROWSER_TEST_F(AppShimMenuControllerBrowserTest, 129 ExtensionUninstallUpdatesMenuBar) { 130 SetUpApps(); 131 132 // This essentially tests that a NSWindowWillCloseNotification gets fired when 133 // an app is uninstalled. We need to close the other windows first since the 134 // menu only changes on a NSWindowWillCloseNotification if there are no other 135 // windows. 136 extensions::AppWindow* app_2_app_window = 137 extensions::AppWindowRegistry::Get(profile()) 138 ->GetAppWindowsForApp(app_2_->id()).front(); 139 app_2_app_window->GetBaseWindow()->Close(); 140 141 chrome::BrowserIterator()->window()->Close(); 142 143 extensions::AppWindow* app_1_app_window = 144 extensions::AppWindowRegistry::Get(profile()) 145 ->GetAppWindowsForApp(app_1_->id()).front(); 146 [[NSNotificationCenter defaultCenter] 147 postNotificationName:NSWindowDidBecomeMainNotification 148 object:app_1_app_window->GetNativeWindow()]; 149 150 CheckHasAppMenus(app_1_); 151 ExtensionService::UninstallExtensionHelper( 152 extension_service(), 153 app_1_->id(), 154 extensions::UNINSTALL_REASON_FOR_TESTING); 155 CheckNoAppMenus(); 156} 157 158} // namespace 159