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