1 // Copyright (c) 2012 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.
5 #import <Carbon/Carbon.h>
6 #import <Cocoa/Cocoa.h>
7 #import <Foundation/Foundation.h>
8 #import <Foundation/NSAppleEventDescriptor.h>
9 #import <objc/message.h>
10 #import <objc/runtime.h>
12 #include "apps/app_window_registry.h"
13 #include "base/command_line.h"
14 #include "base/mac/foundation_util.h"
15 #include "base/mac/scoped_nsobject.h"
16 #include "base/prefs/pref_service.h"
17 #include "chrome/app/chrome_command_ids.h"
18 #import "chrome/browser/app_controller_mac.h"
19 #include "chrome/browser/apps/app_browsertest_util.h"
20 #include "chrome/browser/browser_process.h"
21 #include "chrome/browser/extensions/extension_test_message_listener.h"
22 #include "chrome/browser/profiles/profile_manager.h"
23 #include "chrome/browser/ui/browser.h"
24 #include "chrome/browser/ui/browser_list.h"
25 #include "chrome/browser/ui/browser_window.h"
26 #import "chrome/browser/ui/cocoa/profiles/user_manager_mac.h"
27 #include "chrome/browser/ui/host_desktop.h"
28 #include "chrome/browser/ui/tabs/tab_strip_model.h"
29 #include "chrome/common/chrome_constants.h"
30 #include "chrome/common/chrome_switches.h"
31 #include "chrome/common/pref_names.h"
32 #include "chrome/common/url_constants.h"
33 #include "chrome/test/base/in_process_browser_test.h"
34 #include "chrome/test/base/ui_test_utils.h"
35 #include "content/public/browser/web_contents.h"
36 #include "extensions/common/extension.h"
37 #include "net/test/embedded_test_server/embedded_test_server.h"
41 GURL g_open_shortcut_url = GURL::EmptyGURL();
45 @interface TestOpenShortcutOnStartup : NSObject
46 - (void)applicationWillFinishLaunching:(NSNotification*)notification;
49 @implementation TestOpenShortcutOnStartup
51 - (void)applicationWillFinishLaunching:(NSNotification*)notification {
52 if (!g_open_shortcut_url.is_valid())
55 AppController* controller =
56 base::mac::ObjCCast<AppController>([NSApp delegate]);
57 Method getUrl = class_getInstanceMethod([controller class],
58 @selector(getUrl:withReply:));
63 base::scoped_nsobject<NSAppleEventDescriptor> shortcutEvent(
64 [[NSAppleEventDescriptor alloc]
65 initWithEventClass:kASAppleScriptSuite
66 eventID:kASSubroutineEvent
68 returnID:kAutoGenerateReturnID
69 transactionID:kAnyTransactionID]);
71 [NSString stringWithUTF8String:g_open_shortcut_url.spec().c_str()];
72 [shortcutEvent setParamDescriptor:
73 [NSAppleEventDescriptor descriptorWithString:url]
74 forKeyword:keyDirectObject];
76 method_invoke(controller, getUrl, shortcutEvent.get(), NULL);
83 class AppControllerPlatformAppBrowserTest
84 : public extensions::PlatformAppBrowserTest {
86 AppControllerPlatformAppBrowserTest()
87 : active_browser_list_(BrowserList::GetInstance(
88 chrome::GetActiveDesktop())) {
91 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
92 PlatformAppBrowserTest::SetUpCommandLine(command_line);
93 command_line->AppendSwitchASCII(switches::kAppId,
97 const BrowserList* active_browser_list_;
100 // Test that if only a platform app window is open and no browser windows are
101 // open then a reopen event does nothing.
102 IN_PROC_BROWSER_TEST_F(AppControllerPlatformAppBrowserTest,
103 PlatformAppReopenWithWindows) {
104 base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
105 NSUInteger old_window_count = [[NSApp windows] count];
106 EXPECT_EQ(1u, active_browser_list_->size());
107 [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:YES];
108 // We do not EXPECT_TRUE the result here because the method
109 // deminiaturizes windows manually rather than return YES and have
112 EXPECT_EQ(old_window_count, [[NSApp windows] count]);
113 EXPECT_EQ(1u, active_browser_list_->size());
116 IN_PROC_BROWSER_TEST_F(AppControllerPlatformAppBrowserTest,
117 ActivationFocusesBrowserWindow) {
118 base::scoped_nsobject<AppController> app_controller(
119 [[AppController alloc] init]);
121 ExtensionTestMessageListener listener("Launched", false);
122 const extensions::Extension* app =
123 InstallAndLaunchPlatformApp("minimal");
124 ASSERT_TRUE(listener.WaitUntilSatisfied());
126 NSWindow* app_window = apps::AppWindowRegistry::Get(profile())
127 ->GetAppWindowsForApp(app->id())
130 NSWindow* browser_window = browser()->window()->GetNativeWindow();
132 EXPECT_LE([[NSApp orderedWindows] indexOfObject:app_window],
133 [[NSApp orderedWindows] indexOfObject:browser_window]);
134 [app_controller applicationShouldHandleReopen:NSApp
135 hasVisibleWindows:YES];
136 EXPECT_LE([[NSApp orderedWindows] indexOfObject:browser_window],
137 [[NSApp orderedWindows] indexOfObject:app_window]);
140 class AppControllerWebAppBrowserTest : public InProcessBrowserTest {
142 AppControllerWebAppBrowserTest()
143 : active_browser_list_(BrowserList::GetInstance(
144 chrome::GetActiveDesktop())) {
147 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
148 command_line->AppendSwitchASCII(switches::kApp, GetAppURL());
151 std::string GetAppURL() const {
152 return "http://example.com/";
155 const BrowserList* active_browser_list_;
158 // Test that in web app mode a reopen event opens the app URL.
159 IN_PROC_BROWSER_TEST_F(AppControllerWebAppBrowserTest,
160 WebAppReopenWithNoWindows) {
161 base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
162 EXPECT_EQ(1u, active_browser_list_->size());
163 BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
165 EXPECT_FALSE(result);
166 EXPECT_EQ(2u, active_browser_list_->size());
168 Browser* browser = active_browser_list_->get(0);
170 browser->tab_strip_model()->GetActiveWebContents()->GetURL();
171 EXPECT_EQ(GetAppURL(), current_url.spec());
174 // Called when the ProfileManager has created a profile.
175 void CreateProfileCallback(const base::Closure& quit_closure,
177 Profile::CreateStatus status) {
178 EXPECT_TRUE(profile);
179 EXPECT_NE(Profile::CREATE_STATUS_LOCAL_FAIL, status);
180 EXPECT_NE(Profile::CREATE_STATUS_REMOTE_FAIL, status);
181 // This will be called multiple times. Wait until the profile is initialized
182 // fully to quit the loop.
183 if (status == Profile::CREATE_STATUS_INITIALIZED)
187 void CreateAndWaitForGuestProfile() {
188 ProfileManager::CreateCallback create_callback =
189 base::Bind(&CreateProfileCallback,
190 base::MessageLoop::current()->QuitClosure());
191 g_browser_process->profile_manager()->CreateProfileAsync(
192 ProfileManager::GetGuestProfilePath(),
197 base::RunLoop().Run();
200 class AppControllerNewProfileManagementBrowserTest
201 : public InProcessBrowserTest {
203 AppControllerNewProfileManagementBrowserTest()
204 : active_browser_list_(BrowserList::GetInstance(
205 chrome::GetActiveDesktop())) {
208 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
209 command_line->AppendSwitch(switches::kNewProfileManagement);
212 const BrowserList* active_browser_list_;
215 // Test that for a regular last profile, a reopen event opens a browser.
216 IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
217 RegularProfileReopenWithNoWindows) {
218 base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
219 EXPECT_EQ(1u, active_browser_list_->size());
220 BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
222 EXPECT_FALSE(result);
223 EXPECT_EQ(2u, active_browser_list_->size());
224 EXPECT_FALSE(UserManagerMac::IsShowing());
227 // Test that for a locked last profile, a reopen event opens the User Manager.
228 IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
229 LockedProfileReopenWithNoWindows) {
230 // The User Manager uses the guest profile as its underlying profile. To
231 // minimize flakiness due to the scheduling/descheduling of tasks on the
232 // different threads, pre-initialize the guest profile before it is needed.
233 CreateAndWaitForGuestProfile();
234 base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
236 // Lock the active profile.
237 Profile* profile = [ac lastProfile];
238 ProfileInfoCache& cache =
239 g_browser_process->profile_manager()->GetProfileInfoCache();
240 size_t profile_index = cache.GetIndexOfProfileWithPath(profile->GetPath());
241 cache.SetProfileSigninRequiredAtIndex(profile_index, true);
242 EXPECT_TRUE(cache.ProfileIsSigninRequiredAtIndex(profile_index));
244 EXPECT_EQ(1u, active_browser_list_->size());
245 BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
246 EXPECT_FALSE(result);
248 base::RunLoop().RunUntilIdle();
249 EXPECT_EQ(1u, active_browser_list_->size());
250 EXPECT_TRUE(UserManagerMac::IsShowing());
251 UserManagerMac::Hide();
254 // Test that for a guest last profile, a reopen event opens the User Manager.
255 IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
256 GuestProfileReopenWithNoWindows) {
257 // Create the guest profile, and set it as the last used profile so the
258 // app controller can use it on init.
259 CreateAndWaitForGuestProfile();
260 PrefService* local_state = g_browser_process->local_state();
261 local_state->SetString(prefs::kProfileLastUsed, chrome::kGuestProfileDir);
263 base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
265 Profile* profile = [ac lastProfile];
266 EXPECT_EQ(ProfileManager::GetGuestProfilePath(), profile->GetPath());
267 EXPECT_TRUE(profile->IsGuestSession());
269 EXPECT_EQ(1u, active_browser_list_->size());
270 BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
271 EXPECT_FALSE(result);
273 base::RunLoop().RunUntilIdle();
275 EXPECT_EQ(1u, active_browser_list_->size());
276 EXPECT_TRUE(UserManagerMac::IsShowing());
277 UserManagerMac::Hide();
280 class AppControllerOpenShortcutBrowserTest : public InProcessBrowserTest {
282 AppControllerOpenShortcutBrowserTest() {
285 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
286 // In order to mimic opening shortcut during browser startup, we need to
287 // send the event before -applicationDidFinishLaunching is called, but
288 // after AppController is loaded.
290 // Since -applicationWillFinishLaunching does nothing now, we swizzle it to
291 // our function to send the event. We need to do this early before running
292 // the main message loop.
294 // NSApp does not exist yet. We need to get the AppController using
296 Class appControllerClass = NSClassFromString(@"AppController");
297 Class openShortcutClass = NSClassFromString(@"TestOpenShortcutOnStartup");
299 ASSERT_TRUE(appControllerClass != nil);
300 ASSERT_TRUE(openShortcutClass != nil);
302 SEL targetMethod = @selector(applicationWillFinishLaunching:);
303 Method original = class_getInstanceMethod(appControllerClass,
305 Method destination = class_getInstanceMethod(openShortcutClass,
308 ASSERT_TRUE(original != NULL);
309 ASSERT_TRUE(destination != NULL);
311 method_exchangeImplementations(original, destination);
313 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
314 g_open_shortcut_url = embedded_test_server()->GetURL("/simple.html");
317 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
318 // If the arg is empty, PrepareTestCommandLine() after this function will
319 // append about:blank as default url.
320 command_line->AppendArg(chrome::kChromeUINewTabURL);
324 IN_PROC_BROWSER_TEST_F(AppControllerOpenShortcutBrowserTest,
325 OpenShortcutOnStartup) {
326 EXPECT_EQ(1, browser()->tab_strip_model()->count());
327 EXPECT_EQ(g_open_shortcut_url,
328 browser()->tab_strip_model()->GetActiveWebContents()
329 ->GetLastCommittedURL());