Home | History | Annotate | Download | only in browser
      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.
      4 
      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>
     11 
     12 #include "base/command_line.h"
     13 #include "base/mac/foundation_util.h"
     14 #include "base/mac/scoped_nsobject.h"
     15 #include "base/prefs/pref_service.h"
     16 #include "chrome/app/chrome_command_ids.h"
     17 #import "chrome/browser/app_controller_mac.h"
     18 #include "chrome/browser/apps/app_browsertest_util.h"
     19 #include "chrome/browser/browser_process.h"
     20 #include "chrome/browser/profiles/profile_manager.h"
     21 #include "chrome/browser/ui/browser.h"
     22 #include "chrome/browser/ui/browser_list.h"
     23 #include "chrome/browser/ui/browser_window.h"
     24 #include "chrome/browser/ui/host_desktop.h"
     25 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     26 #include "chrome/browser/ui/user_manager.h"
     27 #include "chrome/common/chrome_constants.h"
     28 #include "chrome/common/chrome_switches.h"
     29 #include "chrome/common/pref_names.h"
     30 #include "chrome/common/url_constants.h"
     31 #include "chrome/test/base/in_process_browser_test.h"
     32 #include "chrome/test/base/ui_test_utils.h"
     33 #include "components/signin/core/common/profile_management_switches.h"
     34 #include "content/public/browser/web_contents.h"
     35 #include "extensions/browser/app_window/app_window_registry.h"
     36 #include "extensions/common/extension.h"
     37 #include "extensions/test/extension_test_message_listener.h"
     38 #include "net/test/embedded_test_server/embedded_test_server.h"
     39 
     40 namespace {
     41 
     42 GURL 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
     81 
     82 namespace {
     83 
     84 class AppControllerPlatformAppBrowserTest
     85     : public extensions::PlatformAppBrowserTest {
     86  protected:
     87   AppControllerPlatformAppBrowserTest()
     88       : active_browser_list_(BrowserList::GetInstance(
     89                                 chrome::GetActiveDesktop())) {
     90   }
     91 
     92   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
     93     PlatformAppBrowserTest::SetUpCommandLine(command_line);
     94     command_line->AppendSwitchASCII(switches::kAppId,
     95                                     "1234");
     96   }
     97 
     98   const BrowserList* active_browser_list_;
     99 };
    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.
    103 IN_PROC_BROWSER_TEST_F(AppControllerPlatformAppBrowserTest,
    104                        PlatformAppReopenWithWindows) {
    105   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
    106   NSUInteger old_window_count = [[NSApp windows] count];
    107   EXPECT_EQ(1u, active_browser_list_->size());
    108   [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.
    112 
    113   EXPECT_EQ(old_window_count, [[NSApp windows] count]);
    114   EXPECT_EQ(1u, active_browser_list_->size());
    115 }
    116 
    117 IN_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 
    127   NSWindow* app_window = extensions::AppWindowRegistry::Get(profile())
    128                              ->GetAppWindowsForApp(app->id())
    129                              .front()
    130                              ->GetNativeWindow();
    131   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 
    141 class AppControllerWebAppBrowserTest : public InProcessBrowserTest {
    142  protected:
    143   AppControllerWebAppBrowserTest()
    144       : active_browser_list_(BrowserList::GetInstance(
    145                                 chrome::GetActiveDesktop())) {
    146   }
    147 
    148   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
    149     command_line->AppendSwitchASCII(switches::kApp, GetAppURL());
    150   }
    151 
    152   std::string GetAppURL() const {
    153     return "http://example.com/";
    154   }
    155 
    156   const BrowserList* active_browser_list_;
    157 };
    158 
    159 // Test that in web app mode a reopen event opens the app URL.
    160 IN_PROC_BROWSER_TEST_F(AppControllerWebAppBrowserTest,
    161                        WebAppReopenWithNoWindows) {
    162   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
    163   EXPECT_EQ(1u, active_browser_list_->size());
    164   BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
    165 
    166   EXPECT_FALSE(result);
    167   EXPECT_EQ(2u, active_browser_list_->size());
    168 
    169   Browser* browser = active_browser_list_->get(0);
    170   GURL current_url =
    171       browser->tab_strip_model()->GetActiveWebContents()->GetURL();
    172   EXPECT_EQ(GetAppURL(), current_url.spec());
    173 }
    174 
    175 // Called when the ProfileManager has created a profile.
    176 void 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 
    188 void 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 
    201 class 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 {
    210     switches::EnableNewProfileManagementForTesting(command_line);
    211   }
    212 
    213   const BrowserList* active_browser_list_;
    214 };
    215 
    216 // Test that for a regular last profile, a reopen event opens a browser.
    217 IN_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());
    225   EXPECT_FALSE(UserManager::IsShowing());
    226 }
    227 
    228 // Test that for a locked last profile, a reopen event opens the User Manager.
    229 IN_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());
    251   EXPECT_TRUE(UserManager::IsShowing());
    252   UserManager::Hide();
    253 }
    254 
    255 // Test that for a guest last profile, a reopen event opens the User Manager.
    256 IN_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());
    277   EXPECT_TRUE(UserManager::IsShowing());
    278   UserManager::Hide();
    279 }
    280 
    281 class 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 
    325 IN_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 
    333 }  // namespace
    334