blob: d920ae86e18966f2eeafe06a81cff252adec5648 [file] [log] [blame]
[email protected]fc44f242012-02-14 16:54:391// 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.
4
[email protected]66791aff2014-04-29 09:45:385#import <Carbon/Carbon.h>
[email protected]fc44f242012-02-14 16:54:396#import <Cocoa/Cocoa.h>
[email protected]66791aff2014-04-29 09:45:387#import <Foundation/Foundation.h>
8#import <Foundation/NSAppleEventDescriptor.h>
9#import <objc/message.h>
10#import <objc/runtime.h>
[email protected]fc44f242012-02-14 16:54:3911
12#include "base/command_line.h"
[email protected]66791aff2014-04-29 09:45:3813#include "base/mac/foundation_util.h"
[email protected]a8522032013-06-24 22:51:4614#include "base/mac/scoped_nsobject.h"
[email protected]7108d912014-01-30 08:10:4515#include "base/prefs/pref_service.h"
[email protected]fc44f242012-02-14 16:54:3916#include "chrome/app/chrome_command_ids.h"
17#import "chrome/browser/app_controller_mac.h"
[email protected]bb4bec02013-08-15 11:26:5818#include "chrome/browser/apps/app_browsertest_util.h"
[email protected]dbb03fb2014-02-15 05:36:3319#include "chrome/browser/browser_process.h"
[email protected]7108d912014-01-30 08:10:4520#include "chrome/browser/profiles/profile_manager.h"
[email protected]52877dbc62012-06-29 22:22:0321#include "chrome/browser/ui/browser.h"
22#include "chrome/browser/ui/browser_list.h"
[email protected]ee9ccfd42013-07-23 02:31:4723#include "chrome/browser/ui/browser_window.h"
[email protected]b4207c42013-02-12 06:44:2024#include "chrome/browser/ui/host_desktop.h"
[email protected]617ee962013-01-29 20:49:1225#include "chrome/browser/ui/tabs/tab_strip_model.h"
mlermane29d0032014-09-24 19:31:2626#include "chrome/browser/ui/user_manager.h"
[email protected]7108d912014-01-30 08:10:4527#include "chrome/common/chrome_constants.h"
28#include "chrome/common/chrome_switches.h"
29#include "chrome/common/pref_names.h"
[email protected]66791aff2014-04-29 09:45:3830#include "chrome/common/url_constants.h"
[email protected]fc44f242012-02-14 16:54:3931#include "chrome/test/base/in_process_browser_test.h"
32#include "chrome/test/base/ui_test_utils.h"
[email protected]29896ee2014-06-17 17:20:5333#include "components/signin/core/common/profile_management_switches.h"
[email protected]fc44f242012-02-14 16:54:3934#include "content/public/browser/web_contents.h"
hashimotoad3c6872014-08-29 09:46:5735#include "extensions/browser/app_window/app_window_registry.h"
[email protected]e4452d32013-11-15 23:07:4136#include "extensions/common/extension.h"
lfg910f2f92014-09-19 05:31:0937#include "extensions/test/extension_test_message_listener.h"
[email protected]66791aff2014-04-29 09:45:3838#include "net/test/embedded_test_server/embedded_test_server.h"
39
40namespace {
41
42GURL g_open_shortcut_url = GURL::EmptyGURL();
43
44} // namespace
45
46@interface TestOpenShortcutOnStartup : NSObject
47- (void)applicationWillFinishLaunching:(NSNotification*)notification;
48@end
49
50@implementation TestOpenShortcutOnStartup
51
52- (void)applicationWillFinishLaunching:(NSNotification*)notification {
53 if (!g_open_shortcut_url.is_valid())
54 return;
55
56 AppController* controller =
57 base::mac::ObjCCast<AppController>([NSApp delegate]);
58 Method getUrl = class_getInstanceMethod([controller class],
59 @selector(getUrl:withReply:));
60
61 if (getUrl == nil)
62 return;
63
64 base::scoped_nsobject<NSAppleEventDescriptor> shortcutEvent(
65 [[NSAppleEventDescriptor alloc]
66 initWithEventClass:kASAppleScriptSuite
67 eventID:kASSubroutineEvent
68 targetDescriptor:nil
69 returnID:kAutoGenerateReturnID
70 transactionID:kAnyTransactionID]);
71 NSString* url =
72 [NSString stringWithUTF8String:g_open_shortcut_url.spec().c_str()];
73 [shortcutEvent setParamDescriptor:
74 [NSAppleEventDescriptor descriptorWithString:url]
75 forKeyword:keyDirectObject];
76
77 method_invoke(controller, getUrl, shortcutEvent.get(), NULL);
78}
79
80@end
[email protected]fc44f242012-02-14 16:54:3981
82namespace {
83
[email protected]ee9ccfd42013-07-23 02:31:4784class AppControllerPlatformAppBrowserTest
85 : public extensions::PlatformAppBrowserTest {
[email protected]fc44f242012-02-14 16:54:3986 protected:
[email protected]b4207c42013-02-12 06:44:2087 AppControllerPlatformAppBrowserTest()
[email protected]7d1a810b2013-06-26 19:51:5988 : active_browser_list_(BrowserList::GetInstance(
[email protected]ed2fa722013-06-25 20:37:3489 chrome::GetActiveDesktop())) {
[email protected]b4207c42013-02-12 06:44:2090 }
[email protected]fc44f242012-02-14 16:54:3991
92 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
[email protected]ee9ccfd42013-07-23 02:31:4793 PlatformAppBrowserTest::SetUpCommandLine(command_line);
[email protected]fc44f242012-02-14 16:54:3994 command_line->AppendSwitchASCII(switches::kAppId,
95 "1234");
96 }
[email protected]b4207c42013-02-12 06:44:2097
[email protected]7d1a810b2013-06-26 19:51:5998 const BrowserList* active_browser_list_;
[email protected]fc44f242012-02-14 16:54:3999};
100
101// Test that if only a platform app window is open and no browser windows are
102// open then a reopen event does nothing.
103IN_PROC_BROWSER_TEST_F(AppControllerPlatformAppBrowserTest,
104 PlatformAppReopenWithWindows) {
[email protected]a8522032013-06-24 22:51:46105 base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
[email protected]fc44f242012-02-14 16:54:39106 NSUInteger old_window_count = [[NSApp windows] count];
[email protected]7d1a810b2013-06-26 19:51:59107 EXPECT_EQ(1u, active_browser_list_->size());
[email protected]a25920ee2013-09-05 19:38:49108 [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:YES];
109 // We do not EXPECT_TRUE the result here because the method
110 // deminiaturizes windows manually rather than return YES and have
111 // AppKit do it.
[email protected]fc44f242012-02-14 16:54:39112
[email protected]fc44f242012-02-14 16:54:39113 EXPECT_EQ(old_window_count, [[NSApp windows] count]);
[email protected]7d1a810b2013-06-26 19:51:59114 EXPECT_EQ(1u, active_browser_list_->size());
[email protected]fc44f242012-02-14 16:54:39115}
116
[email protected]2e29e2232013-07-26 10:40:59117IN_PROC_BROWSER_TEST_F(AppControllerPlatformAppBrowserTest,
118 ActivationFocusesBrowserWindow) {
119 base::scoped_nsobject<AppController> app_controller(
120 [[AppController alloc] init]);
121
122 ExtensionTestMessageListener listener("Launched", false);
123 const extensions::Extension* app =
124 InstallAndLaunchPlatformApp("minimal");
125 ASSERT_TRUE(listener.WaitUntilSatisfied());
126
hashimotoad3c6872014-08-29 09:46:57127 NSWindow* app_window = extensions::AppWindowRegistry::Get(profile())
[email protected]dbb03fb2014-02-15 05:36:33128 ->GetAppWindowsForApp(app->id())
129 .front()
130 ->GetNativeWindow();
[email protected]2e29e2232013-07-26 10:40:59131 NSWindow* browser_window = browser()->window()->GetNativeWindow();
132
133 EXPECT_LE([[NSApp orderedWindows] indexOfObject:app_window],
134 [[NSApp orderedWindows] indexOfObject:browser_window]);
135 [app_controller applicationShouldHandleReopen:NSApp
136 hasVisibleWindows:YES];
137 EXPECT_LE([[NSApp orderedWindows] indexOfObject:browser_window],
138 [[NSApp orderedWindows] indexOfObject:app_window]);
139}
140
[email protected]fc44f242012-02-14 16:54:39141class AppControllerWebAppBrowserTest : public InProcessBrowserTest {
142 protected:
[email protected]b4207c42013-02-12 06:44:20143 AppControllerWebAppBrowserTest()
[email protected]7d1a810b2013-06-26 19:51:59144 : active_browser_list_(BrowserList::GetInstance(
[email protected]ed2fa722013-06-25 20:37:34145 chrome::GetActiveDesktop())) {
[email protected]b4207c42013-02-12 06:44:20146 }
[email protected]fc44f242012-02-14 16:54:39147
148 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
[email protected]90ca44272012-07-18 18:15:48149 command_line->AppendSwitchASCII(switches::kApp, GetAppURL());
[email protected]fc44f242012-02-14 16:54:39150 }
151
152 std::string GetAppURL() const {
153 return "http://example.com/";
154 }
[email protected]b4207c42013-02-12 06:44:20155
[email protected]7d1a810b2013-06-26 19:51:59156 const BrowserList* active_browser_list_;
[email protected]fc44f242012-02-14 16:54:39157};
158
159// Test that in web app mode a reopen event opens the app URL.
160IN_PROC_BROWSER_TEST_F(AppControllerWebAppBrowserTest,
161 WebAppReopenWithNoWindows) {
[email protected]a8522032013-06-24 22:51:46162 base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
[email protected]7d1a810b2013-06-26 19:51:59163 EXPECT_EQ(1u, active_browser_list_->size());
[email protected]fc44f242012-02-14 16:54:39164 BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
165
166 EXPECT_FALSE(result);
[email protected]7d1a810b2013-06-26 19:51:59167 EXPECT_EQ(2u, active_browser_list_->size());
[email protected]fc44f242012-02-14 16:54:39168
[email protected]7d1a810b2013-06-26 19:51:59169 Browser* browser = active_browser_list_->get(0);
[email protected]617ee962013-01-29 20:49:12170 GURL current_url =
171 browser->tab_strip_model()->GetActiveWebContents()->GetURL();
[email protected]fc44f242012-02-14 16:54:39172 EXPECT_EQ(GetAppURL(), current_url.spec());
173}
174
[email protected]7108d912014-01-30 08:10:45175// Called when the ProfileManager has created a profile.
176void CreateProfileCallback(const base::Closure& quit_closure,
177 Profile* profile,
178 Profile::CreateStatus status) {
179 EXPECT_TRUE(profile);
180 EXPECT_NE(Profile::CREATE_STATUS_LOCAL_FAIL, status);
181 EXPECT_NE(Profile::CREATE_STATUS_REMOTE_FAIL, status);
182 // This will be called multiple times. Wait until the profile is initialized
183 // fully to quit the loop.
184 if (status == Profile::CREATE_STATUS_INITIALIZED)
185 quit_closure.Run();
186}
187
188void CreateAndWaitForGuestProfile() {
189 ProfileManager::CreateCallback create_callback =
190 base::Bind(&CreateProfileCallback,
191 base::MessageLoop::current()->QuitClosure());
192 g_browser_process->profile_manager()->CreateProfileAsync(
193 ProfileManager::GetGuestProfilePath(),
194 create_callback,
195 base::string16(),
196 base::string16(),
197 std::string());
198 base::RunLoop().Run();
199}
200
201class AppControllerNewProfileManagementBrowserTest
202 : public InProcessBrowserTest {
203 protected:
204 AppControllerNewProfileManagementBrowserTest()
205 : active_browser_list_(BrowserList::GetInstance(
206 chrome::GetActiveDesktop())) {
207 }
208
209 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
[email protected]29896ee2014-06-17 17:20:53210 switches::EnableNewProfileManagementForTesting(command_line);
[email protected]7108d912014-01-30 08:10:45211 }
212
213 const BrowserList* active_browser_list_;
214};
215
216// Test that for a regular last profile, a reopen event opens a browser.
217IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
218 RegularProfileReopenWithNoWindows) {
219 base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
220 EXPECT_EQ(1u, active_browser_list_->size());
221 BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
222
223 EXPECT_FALSE(result);
224 EXPECT_EQ(2u, active_browser_list_->size());
mlermane29d0032014-09-24 19:31:26225 EXPECT_FALSE(UserManager::IsShowing());
[email protected]7108d912014-01-30 08:10:45226}
227
228// Test that for a locked last profile, a reopen event opens the User Manager.
229IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
230 LockedProfileReopenWithNoWindows) {
231 // The User Manager uses the guest profile as its underlying profile. To
232 // minimize flakiness due to the scheduling/descheduling of tasks on the
233 // different threads, pre-initialize the guest profile before it is needed.
234 CreateAndWaitForGuestProfile();
235 base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
236
237 // Lock the active profile.
238 Profile* profile = [ac lastProfile];
239 ProfileInfoCache& cache =
240 g_browser_process->profile_manager()->GetProfileInfoCache();
241 size_t profile_index = cache.GetIndexOfProfileWithPath(profile->GetPath());
242 cache.SetProfileSigninRequiredAtIndex(profile_index, true);
243 EXPECT_TRUE(cache.ProfileIsSigninRequiredAtIndex(profile_index));
244
245 EXPECT_EQ(1u, active_browser_list_->size());
246 BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
247 EXPECT_FALSE(result);
248
249 base::RunLoop().RunUntilIdle();
250 EXPECT_EQ(1u, active_browser_list_->size());
mlermane29d0032014-09-24 19:31:26251 EXPECT_TRUE(UserManager::IsShowing());
252 UserManager::Hide();
[email protected]7108d912014-01-30 08:10:45253}
254
255// Test that for a guest last profile, a reopen event opens the User Manager.
256IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
257 GuestProfileReopenWithNoWindows) {
258 // Create the guest profile, and set it as the last used profile so the
259 // app controller can use it on init.
260 CreateAndWaitForGuestProfile();
261 PrefService* local_state = g_browser_process->local_state();
262 local_state->SetString(prefs::kProfileLastUsed, chrome::kGuestProfileDir);
263
264 base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
265
266 Profile* profile = [ac lastProfile];
267 EXPECT_EQ(ProfileManager::GetGuestProfilePath(), profile->GetPath());
268 EXPECT_TRUE(profile->IsGuestSession());
269
270 EXPECT_EQ(1u, active_browser_list_->size());
271 BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
272 EXPECT_FALSE(result);
273
274 base::RunLoop().RunUntilIdle();
275
276 EXPECT_EQ(1u, active_browser_list_->size());
mlermane29d0032014-09-24 19:31:26277 EXPECT_TRUE(UserManager::IsShowing());
278 UserManager::Hide();
[email protected]7108d912014-01-30 08:10:45279}
280
[email protected]66791aff2014-04-29 09:45:38281class AppControllerOpenShortcutBrowserTest : public InProcessBrowserTest {
282 protected:
283 AppControllerOpenShortcutBrowserTest() {
284 }
285
286 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
287 // In order to mimic opening shortcut during browser startup, we need to
288 // send the event before -applicationDidFinishLaunching is called, but
289 // after AppController is loaded.
290 //
291 // Since -applicationWillFinishLaunching does nothing now, we swizzle it to
292 // our function to send the event. We need to do this early before running
293 // the main message loop.
294 //
295 // NSApp does not exist yet. We need to get the AppController using
296 // reflection.
297 Class appControllerClass = NSClassFromString(@"AppController");
298 Class openShortcutClass = NSClassFromString(@"TestOpenShortcutOnStartup");
299
300 ASSERT_TRUE(appControllerClass != nil);
301 ASSERT_TRUE(openShortcutClass != nil);
302
303 SEL targetMethod = @selector(applicationWillFinishLaunching:);
304 Method original = class_getInstanceMethod(appControllerClass,
305 targetMethod);
306 Method destination = class_getInstanceMethod(openShortcutClass,
307 targetMethod);
308
309 ASSERT_TRUE(original != NULL);
310 ASSERT_TRUE(destination != NULL);
311
312 method_exchangeImplementations(original, destination);
313
314 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
315 g_open_shortcut_url = embedded_test_server()->GetURL("/simple.html");
316 }
317
318 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
319 // If the arg is empty, PrepareTestCommandLine() after this function will
320 // append about:blank as default url.
321 command_line->AppendArg(chrome::kChromeUINewTabURL);
322 }
323};
324
325IN_PROC_BROWSER_TEST_F(AppControllerOpenShortcutBrowserTest,
326 OpenShortcutOnStartup) {
327 EXPECT_EQ(1, browser()->tab_strip_model()->count());
328 EXPECT_EQ(g_open_shortcut_url,
329 browser()->tab_strip_model()->GetActiveWebContents()
330 ->GetLastCommittedURL());
331}
332
[email protected]fc44f242012-02-14 16:54:39333} // namespace