blob: e73f9f3c4b542c7ec86b4465e484eb566a3c25f4 [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>
avie4d7b6f2015-12-26 00:59:1811#include <stddef.h>
[email protected]fc44f242012-02-14 16:54:3912
13#include "base/command_line.h"
[email protected]66791aff2014-04-29 09:45:3814#include "base/mac/foundation_util.h"
[email protected]a8522032013-06-24 22:51:4615#include "base/mac/scoped_nsobject.h"
gabf64a25e2017-05-12 19:42:5616#include "base/message_loop/message_loop.h"
thestigaf7f4152014-10-31 23:19:1517#include "base/run_loop.h"
lgarron9e6dee22014-11-18 01:03:3918#include "base/strings/sys_string_conversions.h"
19#include "base/strings/utf_string_conversions.h"
jam3f2d3932017-04-26 20:28:5120#include "base/threading/thread_restrictions.h"
[email protected]fc44f242012-02-14 16:54:3921#include "chrome/app/chrome_command_ids.h"
22#import "chrome/browser/app_controller_mac.h"
[email protected]bb4bec02013-08-15 11:26:5823#include "chrome/browser/apps/app_browsertest_util.h"
lgarron9e6dee22014-11-18 01:03:3924#include "chrome/browser/bookmarks/bookmark_model_factory.h"
[email protected]dbb03fb2014-02-15 05:36:3325#include "chrome/browser/browser_process.h"
mlerman8ae56aa2015-04-24 13:56:2726#include "chrome/browser/history/history_service_factory.h"
lwchkg498e92492016-04-23 11:04:1227#include "chrome/browser/profiles/profile_attributes_entry.h"
28#include "chrome/browser/profiles/profile_attributes_storage.h"
[email protected]7108d912014-01-30 08:10:4529#include "chrome/browser/profiles/profile_manager.h"
[email protected]52877dbc62012-06-29 22:22:0330#include "chrome/browser/ui/browser.h"
31#include "chrome/browser/ui/browser_list.h"
thestige80821242015-09-30 23:46:0832#include "chrome/browser/ui/browser_navigator_params.h"
[email protected]ee9ccfd42013-07-23 02:31:4733#include "chrome/browser/ui/browser_window.h"
lgarron9e6dee22014-11-18 01:03:3934#include "chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.h"
mlerman8ae56aa2015-04-24 13:56:2735#include "chrome/browser/ui/cocoa/history_menu_bridge.h"
lgrey92aad3c2016-12-10 01:22:1836#include "chrome/browser/ui/cocoa/test/run_loop_testing.h"
[email protected]617ee962013-01-29 20:49:1237#include "chrome/browser/ui/tabs/tab_strip_model.h"
mlermane29d0032014-09-24 19:31:2638#include "chrome/browser/ui/user_manager.h"
[email protected]7108d912014-01-30 08:10:4539#include "chrome/common/chrome_constants.h"
40#include "chrome/common/chrome_switches.h"
41#include "chrome/common/pref_names.h"
[email protected]66791aff2014-04-29 09:45:3842#include "chrome/common/url_constants.h"
[email protected]fc44f242012-02-14 16:54:3943#include "chrome/test/base/in_process_browser_test.h"
erikchen600f7962014-12-12 00:17:3844#include "chrome/test/base/ui_test_utils.h"
tapted574f09c2015-05-19 13:08:0845#include "components/bookmarks/browser/bookmark_model.h"
lgarron9e6dee22014-11-18 01:03:3946#include "components/bookmarks/test/bookmark_test_helpers.h"
brettwb1fc1b82016-02-02 00:19:0847#include "components/prefs/pref_service.h"
tmartino82e55782017-01-10 22:30:1748#include "content/public/browser/navigation_controller.h"
[email protected]fc44f242012-02-14 16:54:3949#include "content/public/browser/web_contents.h"
erikchen600f7962014-12-12 00:17:3850#include "content/public/test/browser_test_utils.h"
erikchen19ee3922014-10-31 19:14:2251#include "content/public/test/test_navigation_observer.h"
hashimotoad3c6872014-08-29 09:46:5752#include "extensions/browser/app_window/app_window_registry.h"
[email protected]e4452d32013-11-15 23:07:4153#include "extensions/common/extension.h"
lfg910f2f92014-09-19 05:31:0954#include "extensions/test/extension_test_message_listener.h"
[email protected]66791aff2014-04-29 09:45:3855#include "net/test/embedded_test_server/embedded_test_server.h"
56
Adam Riceff26dcf2017-08-14 05:01:5257using base::SysUTF16ToNSString;
58
[email protected]66791aff2014-04-29 09:45:3859namespace {
60
Scott Violet1f106b582017-07-12 15:49:5861GURL g_open_shortcut_url = GURL::EmptyGURL();
[email protected]66791aff2014-04-29 09:45:3862
erikchen19ee3922014-10-31 19:14:2263// Returns an Apple Event that instructs the application to open |url|.
64NSAppleEventDescriptor* AppleEventToOpenUrl(const GURL& url) {
65 NSAppleEventDescriptor* shortcut_event = [[[NSAppleEventDescriptor alloc]
66 initWithEventClass:kASAppleScriptSuite
67 eventID:kASSubroutineEvent
68 targetDescriptor:nil
69 returnID:kAutoGenerateReturnID
70 transactionID:kAnyTransactionID] autorelease];
71 NSString* url_string = [NSString stringWithUTF8String:url.spec().c_str()];
72 [shortcut_event setParamDescriptor:[NSAppleEventDescriptor
73 descriptorWithString:url_string]
74 forKeyword:keyDirectObject];
75 return shortcut_event;
76}
77
78// Instructs the NSApp's delegate to open |url|.
79void SendAppleEventToOpenUrlToAppController(const GURL& url) {
80 AppController* controller =
81 base::mac::ObjCCast<AppController>([NSApp delegate]);
82 Method get_url =
83 class_getInstanceMethod([controller class], @selector(getUrl:withReply:));
84
85 ASSERT_TRUE(get_url);
86
87 NSAppleEventDescriptor* shortcut_event = AppleEventToOpenUrl(url);
88
89 method_invoke(controller, get_url, shortcut_event, NULL);
90}
91
mlerman8ae56aa2015-04-24 13:56:2792void RunClosureWhenProfileInitialized(const base::Closure& closure,
93 Profile* profile,
94 Profile::CreateStatus status) {
95 if (status == Profile::CREATE_STATUS_INITIALIZED)
96 closure.Run();
97}
98
[email protected]66791aff2014-04-29 09:45:3899} // namespace
100
101@interface TestOpenShortcutOnStartup : NSObject
102- (void)applicationWillFinishLaunching:(NSNotification*)notification;
103@end
104
105@implementation TestOpenShortcutOnStartup
106
107- (void)applicationWillFinishLaunching:(NSNotification*)notification {
Scott Violet1f106b582017-07-12 15:49:58108 if (!g_open_shortcut_url.is_valid())
109 return;
110
erikchen19ee3922014-10-31 19:14:22111 SendAppleEventToOpenUrlToAppController(g_open_shortcut_url);
[email protected]66791aff2014-04-29 09:45:38112}
113
114@end
[email protected]fc44f242012-02-14 16:54:39115
116namespace {
117
[email protected]ee9ccfd42013-07-23 02:31:47118class AppControllerPlatformAppBrowserTest
119 : public extensions::PlatformAppBrowserTest {
[email protected]fc44f242012-02-14 16:54:39120 protected:
[email protected]b4207c42013-02-12 06:44:20121 AppControllerPlatformAppBrowserTest()
scottmg0d8e4ab2016-01-28 00:34:55122 : active_browser_list_(BrowserList::GetInstance()) {}
[email protected]fc44f242012-02-14 16:54:39123
avi3ef9ec9e2014-12-22 22:50:17124 void SetUpCommandLine(base::CommandLine* command_line) override {
[email protected]ee9ccfd42013-07-23 02:31:47125 PlatformAppBrowserTest::SetUpCommandLine(command_line);
[email protected]fc44f242012-02-14 16:54:39126 command_line->AppendSwitchASCII(switches::kAppId,
127 "1234");
128 }
[email protected]b4207c42013-02-12 06:44:20129
[email protected]7d1a810b2013-06-26 19:51:59130 const BrowserList* active_browser_list_;
[email protected]fc44f242012-02-14 16:54:39131};
132
133// Test that if only a platform app window is open and no browser windows are
134// open then a reopen event does nothing.
135IN_PROC_BROWSER_TEST_F(AppControllerPlatformAppBrowserTest,
136 PlatformAppReopenWithWindows) {
[email protected]a8522032013-06-24 22:51:46137 base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
[email protected]fc44f242012-02-14 16:54:39138 NSUInteger old_window_count = [[NSApp windows] count];
[email protected]7d1a810b2013-06-26 19:51:59139 EXPECT_EQ(1u, active_browser_list_->size());
[email protected]a25920ee2013-09-05 19:38:49140 [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:YES];
141 // We do not EXPECT_TRUE the result here because the method
142 // deminiaturizes windows manually rather than return YES and have
143 // AppKit do it.
[email protected]fc44f242012-02-14 16:54:39144
[email protected]fc44f242012-02-14 16:54:39145 EXPECT_EQ(old_window_count, [[NSApp windows] count]);
[email protected]7d1a810b2013-06-26 19:51:59146 EXPECT_EQ(1u, active_browser_list_->size());
[email protected]fc44f242012-02-14 16:54:39147}
148
[email protected]2e29e2232013-07-26 10:40:59149IN_PROC_BROWSER_TEST_F(AppControllerPlatformAppBrowserTest,
150 ActivationFocusesBrowserWindow) {
151 base::scoped_nsobject<AppController> app_controller(
152 [[AppController alloc] init]);
153
154 ExtensionTestMessageListener listener("Launched", false);
155 const extensions::Extension* app =
156 InstallAndLaunchPlatformApp("minimal");
157 ASSERT_TRUE(listener.WaitUntilSatisfied());
158
hashimotoad3c6872014-08-29 09:46:57159 NSWindow* app_window = extensions::AppWindowRegistry::Get(profile())
[email protected]dbb03fb2014-02-15 05:36:33160 ->GetAppWindowsForApp(app->id())
161 .front()
162 ->GetNativeWindow();
[email protected]2e29e2232013-07-26 10:40:59163 NSWindow* browser_window = browser()->window()->GetNativeWindow();
164
andybons346c7c62015-08-13 15:18:03165 chrome::testing::NSRunLoopRunAllPending();
[email protected]2e29e2232013-07-26 10:40:59166 EXPECT_LE([[NSApp orderedWindows] indexOfObject:app_window],
167 [[NSApp orderedWindows] indexOfObject:browser_window]);
168 [app_controller applicationShouldHandleReopen:NSApp
169 hasVisibleWindows:YES];
andybons346c7c62015-08-13 15:18:03170 chrome::testing::NSRunLoopRunAllPending();
[email protected]2e29e2232013-07-26 10:40:59171 EXPECT_LE([[NSApp orderedWindows] indexOfObject:browser_window],
172 [[NSApp orderedWindows] indexOfObject:app_window]);
173}
174
[email protected]fc44f242012-02-14 16:54:39175class AppControllerWebAppBrowserTest : public InProcessBrowserTest {
176 protected:
[email protected]b4207c42013-02-12 06:44:20177 AppControllerWebAppBrowserTest()
scottmg0d8e4ab2016-01-28 00:34:55178 : active_browser_list_(BrowserList::GetInstance()) {}
[email protected]fc44f242012-02-14 16:54:39179
avi3ef9ec9e2014-12-22 22:50:17180 void SetUpCommandLine(base::CommandLine* command_line) override {
[email protected]90ca44272012-07-18 18:15:48181 command_line->AppendSwitchASCII(switches::kApp, GetAppURL());
[email protected]fc44f242012-02-14 16:54:39182 }
183
184 std::string GetAppURL() const {
185 return "http://example.com/";
186 }
[email protected]b4207c42013-02-12 06:44:20187
[email protected]7d1a810b2013-06-26 19:51:59188 const BrowserList* active_browser_list_;
[email protected]fc44f242012-02-14 16:54:39189};
190
191// Test that in web app mode a reopen event opens the app URL.
192IN_PROC_BROWSER_TEST_F(AppControllerWebAppBrowserTest,
193 WebAppReopenWithNoWindows) {
[email protected]a8522032013-06-24 22:51:46194 base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
[email protected]7d1a810b2013-06-26 19:51:59195 EXPECT_EQ(1u, active_browser_list_->size());
[email protected]fc44f242012-02-14 16:54:39196 BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
197
198 EXPECT_FALSE(result);
[email protected]7d1a810b2013-06-26 19:51:59199 EXPECT_EQ(2u, active_browser_list_->size());
[email protected]fc44f242012-02-14 16:54:39200
[email protected]7d1a810b2013-06-26 19:51:59201 Browser* browser = active_browser_list_->get(0);
[email protected]617ee962013-01-29 20:49:12202 GURL current_url =
203 browser->tab_strip_model()->GetActiveWebContents()->GetURL();
[email protected]fc44f242012-02-14 16:54:39204 EXPECT_EQ(GetAppURL(), current_url.spec());
205}
206
[email protected]7108d912014-01-30 08:10:45207// Called when the ProfileManager has created a profile.
208void CreateProfileCallback(const base::Closure& quit_closure,
209 Profile* profile,
210 Profile::CreateStatus status) {
211 EXPECT_TRUE(profile);
212 EXPECT_NE(Profile::CREATE_STATUS_LOCAL_FAIL, status);
213 EXPECT_NE(Profile::CREATE_STATUS_REMOTE_FAIL, status);
214 // This will be called multiple times. Wait until the profile is initialized
215 // fully to quit the loop.
216 if (status == Profile::CREATE_STATUS_INITIALIZED)
217 quit_closure.Run();
218}
219
mlermanb8df6e82015-01-23 19:55:34220void CreateAndWaitForSystemProfile() {
[email protected]7108d912014-01-30 08:10:45221 ProfileManager::CreateCallback create_callback =
222 base::Bind(&CreateProfileCallback,
ki.stfuc4f8e242015-10-09 20:40:20223 base::MessageLoop::current()->QuitWhenIdleClosure());
[email protected]7108d912014-01-30 08:10:45224 g_browser_process->profile_manager()->CreateProfileAsync(
mlermanb8df6e82015-01-23 19:55:34225 ProfileManager::GetSystemProfilePath(),
[email protected]7108d912014-01-30 08:10:45226 create_callback,
227 base::string16(),
lwchkg1f62d242015-10-29 00:50:43228 std::string(),
[email protected]7108d912014-01-30 08:10:45229 std::string());
230 base::RunLoop().Run();
231}
232
233class AppControllerNewProfileManagementBrowserTest
234 : public InProcessBrowserTest {
235 protected:
236 AppControllerNewProfileManagementBrowserTest()
scottmg0d8e4ab2016-01-28 00:34:55237 : active_browser_list_(BrowserList::GetInstance()) {}
[email protected]7108d912014-01-30 08:10:45238
[email protected]7108d912014-01-30 08:10:45239 const BrowserList* active_browser_list_;
240};
241
242// Test that for a regular last profile, a reopen event opens a browser.
243IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
244 RegularProfileReopenWithNoWindows) {
245 base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
246 EXPECT_EQ(1u, active_browser_list_->size());
247 BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
248
249 EXPECT_FALSE(result);
250 EXPECT_EQ(2u, active_browser_list_->size());
mlermane29d0032014-09-24 19:31:26251 EXPECT_FALSE(UserManager::IsShowing());
[email protected]7108d912014-01-30 08:10:45252}
253
254// Test that for a locked last profile, a reopen event opens the User Manager.
255IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
256 LockedProfileReopenWithNoWindows) {
mlermanb8df6e82015-01-23 19:55:34257 // The User Manager uses the system profile as its underlying profile. To
[email protected]7108d912014-01-30 08:10:45258 // minimize flakiness due to the scheduling/descheduling of tasks on the
259 // different threads, pre-initialize the guest profile before it is needed.
mlermanb8df6e82015-01-23 19:55:34260 CreateAndWaitForSystemProfile();
[email protected]7108d912014-01-30 08:10:45261 base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
262
263 // Lock the active profile.
Francois Doraye6fb2d02017-10-18 21:29:13264 base::ScopedAllowBlockingForTesting allow_blocking;
[email protected]7108d912014-01-30 08:10:45265 Profile* profile = [ac lastProfile];
lwchkg9c183942016-03-13 06:29:54266 ProfileAttributesEntry* entry;
267 ASSERT_TRUE(g_browser_process->profile_manager()->
268 GetProfileAttributesStorage().
269 GetProfileAttributesWithPath(profile->GetPath(), &entry));
270 entry->SetIsSigninRequired(true);
271 EXPECT_TRUE(entry->IsSigninRequired());
[email protected]7108d912014-01-30 08:10:45272
273 EXPECT_EQ(1u, active_browser_list_->size());
274 BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
275 EXPECT_FALSE(result);
276
277 base::RunLoop().RunUntilIdle();
278 EXPECT_EQ(1u, active_browser_list_->size());
mlermane29d0032014-09-24 19:31:26279 EXPECT_TRUE(UserManager::IsShowing());
280 UserManager::Hide();
[email protected]7108d912014-01-30 08:10:45281}
282
guidoubbb4787a2016-12-15 12:34:43283#if defined(ADDRESS_SANITIZER)
284// Flaky under ASAN. See https://crbug.com/674475.
285#define MAYBE_GuestProfileReopenWithNoWindows DISABLED_GuestProfileReopenWithNoWindows
286#else
287#define MAYBE_GuestProfileReopenWithNoWindows GuestProfileReopenWithNoWindows
288#endif
[email protected]7108d912014-01-30 08:10:45289// Test that for a guest last profile, a reopen event opens the User Manager.
290IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
guidoubbb4787a2016-12-15 12:34:43291 MAYBE_GuestProfileReopenWithNoWindows) {
mlermanb8df6e82015-01-23 19:55:34292 // Create the system profile. Set the guest as the last used profile so the
[email protected]7108d912014-01-30 08:10:45293 // app controller can use it on init.
mlermanb8df6e82015-01-23 19:55:34294 CreateAndWaitForSystemProfile();
[email protected]7108d912014-01-30 08:10:45295 PrefService* local_state = g_browser_process->local_state();
296 local_state->SetString(prefs::kProfileLastUsed, chrome::kGuestProfileDir);
297
298 base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
299
Francois Doraye6fb2d02017-10-18 21:29:13300 base::ScopedAllowBlockingForTesting allow_blocking;
[email protected]7108d912014-01-30 08:10:45301 Profile* profile = [ac lastProfile];
302 EXPECT_EQ(ProfileManager::GetGuestProfilePath(), profile->GetPath());
303 EXPECT_TRUE(profile->IsGuestSession());
304
305 EXPECT_EQ(1u, active_browser_list_->size());
306 BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
307 EXPECT_FALSE(result);
308
309 base::RunLoop().RunUntilIdle();
310
311 EXPECT_EQ(1u, active_browser_list_->size());
mlermane29d0032014-09-24 19:31:26312 EXPECT_TRUE(UserManager::IsShowing());
313 UserManager::Hide();
[email protected]7108d912014-01-30 08:10:45314}
315
guidoubbb4787a2016-12-15 12:34:43316#if defined(ADDRESS_SANITIZER)
317// Flaky under ASAN. See https://crbug.com/674475.
318#define MAYBE_AboutChromeForcesUserManager DISABLED_AboutChromeForcesUserManager
319#else
320#define MAYBE_AboutChromeForcesUserManager AboutChromeForcesUserManager
321#endif
mlermane6e040a2014-10-31 00:53:21322IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
guidoubbb4787a2016-12-15 12:34:43323 MAYBE_AboutChromeForcesUserManager) {
mlermane6e040a2014-10-31 00:53:21324 base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
325
326 // Create the guest profile, and set it as the last used profile so the
327 // app controller can use it on init.
mlermanb8df6e82015-01-23 19:55:34328 CreateAndWaitForSystemProfile();
mlermane6e040a2014-10-31 00:53:21329 PrefService* local_state = g_browser_process->local_state();
330 local_state->SetString(prefs::kProfileLastUsed, chrome::kGuestProfileDir);
331
332 // Prohibiting guest mode forces the user manager flow for About Chrome.
333 local_state->SetBoolean(prefs::kBrowserGuestModeEnabled, false);
334
Francois Doraye6fb2d02017-10-18 21:29:13335 base::ScopedAllowBlockingForTesting allow_blocking;
mlermane6e040a2014-10-31 00:53:21336 Profile* guest_profile = [ac lastProfile];
337 EXPECT_EQ(ProfileManager::GetGuestProfilePath(), guest_profile->GetPath());
338 EXPECT_TRUE(guest_profile->IsGuestSession());
339
340 // Tell the browser to open About Chrome.
341 EXPECT_EQ(1u, active_browser_list_->size());
342 [ac orderFrontStandardAboutPanel:NSApp];
343
344 base::RunLoop().RunUntilIdle();
345
346 // No new browser is opened; the User Manager opens instead.
347 EXPECT_EQ(1u, active_browser_list_->size());
348 EXPECT_TRUE(UserManager::IsShowing());
349
350 UserManager::Hide();
351}
352
[email protected]66791aff2014-04-29 09:45:38353class AppControllerOpenShortcutBrowserTest : public InProcessBrowserTest {
354 protected:
355 AppControllerOpenShortcutBrowserTest() {
356 }
357
Daniel Chenga542fca2014-10-21 09:51:29358 void SetUpInProcessBrowserTestFixture() override {
[email protected]66791aff2014-04-29 09:45:38359 // In order to mimic opening shortcut during browser startup, we need to
360 // send the event before -applicationDidFinishLaunching is called, but
361 // after AppController is loaded.
362 //
363 // Since -applicationWillFinishLaunching does nothing now, we swizzle it to
364 // our function to send the event. We need to do this early before running
365 // the main message loop.
366 //
367 // NSApp does not exist yet. We need to get the AppController using
368 // reflection.
369 Class appControllerClass = NSClassFromString(@"AppController");
370 Class openShortcutClass = NSClassFromString(@"TestOpenShortcutOnStartup");
371
372 ASSERT_TRUE(appControllerClass != nil);
373 ASSERT_TRUE(openShortcutClass != nil);
374
375 SEL targetMethod = @selector(applicationWillFinishLaunching:);
376 Method original = class_getInstanceMethod(appControllerClass,
377 targetMethod);
378 Method destination = class_getInstanceMethod(openShortcutClass,
379 targetMethod);
380
381 ASSERT_TRUE(original != NULL);
382 ASSERT_TRUE(destination != NULL);
383
384 method_exchangeImplementations(original, destination);
Scott Violet1f106b582017-07-12 15:49:58385
386 ASSERT_TRUE(embedded_test_server()->Start());
387 g_open_shortcut_url = embedded_test_server()->GetURL("/simple.html");
[email protected]66791aff2014-04-29 09:45:38388 }
389
avi3ef9ec9e2014-12-22 22:50:17390 void SetUpCommandLine(base::CommandLine* command_line) override {
[email protected]66791aff2014-04-29 09:45:38391 // If the arg is empty, PrepareTestCommandLine() after this function will
392 // append about:blank as default url.
393 command_line->AppendArg(chrome::kChromeUINewTabURL);
394 }
395};
396
397IN_PROC_BROWSER_TEST_F(AppControllerOpenShortcutBrowserTest,
398 OpenShortcutOnStartup) {
tmartino82e55782017-01-10 22:30:17399 // The two tabs expected are the Welcome page and the desired URL.
400 EXPECT_EQ(2, browser()->tab_strip_model()->count());
[email protected]66791aff2014-04-29 09:45:38401 EXPECT_EQ(g_open_shortcut_url,
402 browser()->tab_strip_model()->GetActiveWebContents()
403 ->GetLastCommittedURL());
404}
405
erikchen19ee3922014-10-31 19:14:22406class AppControllerReplaceNTPBrowserTest : public InProcessBrowserTest {
407 protected:
408 AppControllerReplaceNTPBrowserTest() {}
409
410 void SetUpInProcessBrowserTestFixture() override {
svaldeza01f7d92015-11-18 17:47:56411 ASSERT_TRUE(embedded_test_server()->Start());
erikchen19ee3922014-10-31 19:14:22412 }
413
avi3ef9ec9e2014-12-22 22:50:17414 void SetUpCommandLine(base::CommandLine* command_line) override {
erikchen19ee3922014-10-31 19:14:22415 // If the arg is empty, PrepareTestCommandLine() after this function will
416 // append about:blank as default url.
417 command_line->AppendArg(chrome::kChromeUINewTabURL);
418 }
419};
420
421// Tests that when a GURL is opened after startup, it replaces the NTP.
422IN_PROC_BROWSER_TEST_F(AppControllerReplaceNTPBrowserTest,
423 ReplaceNTPAfterStartup) {
424 // Ensure that there is exactly 1 tab showing, and the tab is the NTP.
425 GURL ntp(chrome::kChromeUINewTabURL);
426 EXPECT_EQ(1, browser()->tab_strip_model()->count());
tmartino82e55782017-01-10 22:30:17427 browser()->tab_strip_model()->GetActiveWebContents()->GetController().LoadURL(
428 GURL(chrome::kChromeUINewTabURL), content::Referrer(),
429 ui::PageTransition::PAGE_TRANSITION_LINK, std::string());
430
431 // Wait for one navigation on the active web contents.
432 content::TestNavigationObserver ntp_navigation_observer(
433 browser()->tab_strip_model()->GetActiveWebContents());
434 ntp_navigation_observer.Wait();
435
erikchen19ee3922014-10-31 19:14:22436 EXPECT_EQ(ntp,
437 browser()
438 ->tab_strip_model()
439 ->GetActiveWebContents()
440 ->GetLastCommittedURL());
441
442 GURL simple(embedded_test_server()->GetURL("/simple.html"));
443 SendAppleEventToOpenUrlToAppController(simple);
444
erikchen19ee3922014-10-31 19:14:22445 EXPECT_EQ(1, browser()->tab_strip_model()->count());
tmartino82e55782017-01-10 22:30:17446 content::TestNavigationObserver event_navigation_observer(
447 browser()->tab_strip_model()->GetActiveWebContents());
448 event_navigation_observer.Wait();
erikchen19ee3922014-10-31 19:14:22449
450 EXPECT_EQ(simple,
451 browser()
452 ->tab_strip_model()
453 ->GetActiveWebContents()
454 ->GetLastCommittedURL());
455}
456
lgarron9e6dee22014-11-18 01:03:39457class AppControllerMainMenuBrowserTest : public InProcessBrowserTest {
458 protected:
459 AppControllerMainMenuBrowserTest() {
460 }
461};
462
463IN_PROC_BROWSER_TEST_F(AppControllerMainMenuBrowserTest,
mlerman8ae56aa2015-04-24 13:56:27464 HistoryMenuResetAfterProfileDeletion) {
465 ProfileManager* profile_manager = g_browser_process->profile_manager();
tapted676995d2016-04-18 11:32:29466 AppController* ac =
467 base::mac::ObjCCastStrict<AppController>([NSApp delegate]);
mlerman8ae56aa2015-04-24 13:56:27468
469 // Use the existing profile as profile 1.
470 Profile* profile1 = browser()->profile();
471
472 // Create profile 2.
473 base::FilePath profile2_path =
474 profile_manager->GenerateNextProfileDirectoryPath();
475 base::RunLoop run_loop;
476 profile_manager->CreateProfileAsync(
477 profile2_path,
478 base::Bind(&RunClosureWhenProfileInitialized,
479 run_loop.QuitClosure()),
lwchkg1f62d242015-10-29 00:50:43480 base::string16(),
481 std::string(),
482 std::string());
mlerman8ae56aa2015-04-24 13:56:27483 run_loop.Run();
484 Profile* profile2 = profile_manager->GetProfileByPath(profile2_path);
485 ASSERT_TRUE(profile2);
486
487 // Switch the controller to profile1.
488 [ac windowChangedToProfile:profile1];
489 base::RunLoop().RunUntilIdle();
490
491 // Verify the controller's History Menu corresponds to profile1.
492 EXPECT_TRUE([ac historyMenuBridge]->service());
493 EXPECT_EQ([ac historyMenuBridge]->service(),
494 HistoryServiceFactory::GetForProfile(profile1,
495 ServiceAccessType::EXPLICIT_ACCESS));
496
497 // Load profile2's History Service backend so it will be assigned to the
498 // HistoryMenuBridge when windowChangedToProfile is called, or else this test
499 // will fail flaky.
500 ui_test_utils::WaitForHistoryToLoad(
501 HistoryServiceFactory::GetForProfile(profile2,
502 ServiceAccessType::EXPLICIT_ACCESS));
503 // Switch the controller to profile2.
504 [ac windowChangedToProfile:profile2];
505 base::RunLoop().RunUntilIdle();
506
507 // Verify the controller's History Menu has changed.
508 EXPECT_TRUE([ac historyMenuBridge]->service());
509 EXPECT_EQ([ac historyMenuBridge]->service(),
510 HistoryServiceFactory::GetForProfile(profile2,
511 ServiceAccessType::EXPLICIT_ACCESS));
512 EXPECT_NE(
513 HistoryServiceFactory::GetForProfile(profile1,
514 ServiceAccessType::EXPLICIT_ACCESS),
515 HistoryServiceFactory::GetForProfile(profile2,
516 ServiceAccessType::EXPLICIT_ACCESS));
517
518 // Delete profile2.
519 profile_manager->ScheduleProfileForDeletion(
520 profile2->GetPath(), ProfileManager::CreateCallback());
521 base::RunLoop().RunUntilIdle();
522
523 // Verify the controller's history is back to profile1.
524 EXPECT_EQ([ac historyMenuBridge]->service(),
525 HistoryServiceFactory::GetForProfile(profile1,
526 ServiceAccessType::EXPLICIT_ACCESS));
527}
528
529IN_PROC_BROWSER_TEST_F(AppControllerMainMenuBrowserTest,
lgarron9e6dee22014-11-18 01:03:39530 BookmarksMenuIsRestoredAfterProfileSwitch) {
531 ProfileManager* profile_manager = g_browser_process->profile_manager();
532 base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
533 [ac awakeFromNib];
534
535 // Constants for bookmarks that we will create later.
536 const base::string16 title1(base::ASCIIToUTF16("Dinosaur Comics"));
537 const GURL url1("http://qwantz.com//");
538
539 const base::string16 title2(base::ASCIIToUTF16("XKCD"));
540 const GURL url2("https://www.xkcd.com/");
541
542 // Use the existing profile as profile 1.
543 Profile* profile1 = browser()->profile();
544 bookmarks::test::WaitForBookmarkModelToLoad(
pke3e0d5c92016-08-08 09:07:30545 BookmarkModelFactory::GetForBrowserContext(profile1));
lgarron9e6dee22014-11-18 01:03:39546
547 // Create profile 2.
Francois Doraye6fb2d02017-10-18 21:29:13548 base::ScopedAllowBlockingForTesting allow_blocking;
lgarron9e6dee22014-11-18 01:03:39549 base::FilePath path2 = profile_manager->GenerateNextProfileDirectoryPath();
550 Profile* profile2 =
551 Profile::CreateProfile(path2, NULL, Profile::CREATE_MODE_SYNCHRONOUS);
552 profile_manager->RegisterTestingProfile(profile2, false, true);
553 bookmarks::test::WaitForBookmarkModelToLoad(
pke3e0d5c92016-08-08 09:07:30554 BookmarkModelFactory::GetForBrowserContext(profile2));
lgarron9e6dee22014-11-18 01:03:39555
556 // Switch to profile 1, create bookmark 1 and force the menu to build.
557 [ac windowChangedToProfile:profile1];
558 [ac bookmarkMenuBridge]->GetBookmarkModel()->AddURL(
559 [ac bookmarkMenuBridge]->GetBookmarkModel()->bookmark_bar_node(),
560 0, title1, url1);
561 [ac bookmarkMenuBridge]->BuildMenu();
562
563 // Switch to profile 2, create bookmark 2 and force the menu to build.
564 [ac windowChangedToProfile:profile2];
565 [ac bookmarkMenuBridge]->GetBookmarkModel()->AddURL(
566 [ac bookmarkMenuBridge]->GetBookmarkModel()->bookmark_bar_node(),
567 0, title2, url2);
568 [ac bookmarkMenuBridge]->BuildMenu();
569
570 // Test that only bookmark 2 is shown.
571 EXPECT_FALSE([[ac bookmarkMenuBridge]->BookmarkMenu() itemWithTitle:
572 SysUTF16ToNSString(title1)]);
573 EXPECT_TRUE([[ac bookmarkMenuBridge]->BookmarkMenu() itemWithTitle:
574 SysUTF16ToNSString(title2)]);
575
576 // Switch *back* to profile 1 and *don't* force the menu to build.
577 [ac windowChangedToProfile:profile1];
578
579 // Test that only bookmark 1 is shown in the restored menu.
580 EXPECT_TRUE([[ac bookmarkMenuBridge]->BookmarkMenu() itemWithTitle:
581 SysUTF16ToNSString(title1)]);
582 EXPECT_FALSE([[ac bookmarkMenuBridge]->BookmarkMenu() itemWithTitle:
583 SysUTF16ToNSString(title2)]);
584}
585
[email protected]fc44f242012-02-14 16:54:39586} // namespace
erikchen600f7962014-12-12 00:17:38587
588//--------------------------AppControllerHandoffBrowserTest---------------------
589
590static GURL g_handoff_url;
591
592@interface AppController (BrowserTest)
593- (BOOL)new_shouldUseHandoff;
594- (void)new_passURLToHandoffManager:(const GURL&)handoffURL;
595@end
596
597@implementation AppController (BrowserTest)
598- (BOOL)new_shouldUseHandoff {
599 return YES;
600}
601
602- (void)new_passURLToHandoffManager:(const GURL&)handoffURL {
603 g_handoff_url = handoffURL;
604}
605@end
606
607namespace {
608
609class AppControllerHandoffBrowserTest : public InProcessBrowserTest {
610 protected:
611 AppControllerHandoffBrowserTest() {}
612
613 // Exchanges the implementations of the two selectors on the class
614 // AppController.
615 void ExchangeSelectors(SEL originalMethod, SEL newMethod) {
616 Class appControllerClass = NSClassFromString(@"AppController");
617
618 ASSERT_TRUE(appControllerClass != nil);
619
620 Method original =
621 class_getInstanceMethod(appControllerClass, originalMethod);
622 Method destination = class_getInstanceMethod(appControllerClass, newMethod);
623
624 ASSERT_TRUE(original != NULL);
625 ASSERT_TRUE(destination != NULL);
626
627 method_exchangeImplementations(original, destination);
628 }
629
630 // Swizzle Handoff related implementations.
631 void SetUpInProcessBrowserTestFixture() override {
632 // Handoff is only available on OSX 10.10+. This swizzle makes the logic
633 // run on all OSX versions.
634 SEL originalMethod = @selector(shouldUseHandoff);
635 SEL newMethod = @selector(new_shouldUseHandoff);
636 ExchangeSelectors(originalMethod, newMethod);
637
638 // This swizzle intercepts the URL that would be sent to the Handoff
639 // Manager, and instead puts it into a variable accessible to this test.
640 originalMethod = @selector(passURLToHandoffManager:);
641 newMethod = @selector(new_passURLToHandoffManager:);
642 ExchangeSelectors(originalMethod, newMethod);
643 }
644
645 // Closes the tab, and waits for the close to finish.
646 void CloseTab(Browser* browser, int index) {
647 content::WebContentsDestroyedWatcher destroyed_watcher(
648 browser->tab_strip_model()->GetWebContentsAt(index));
649 browser->tab_strip_model()->CloseWebContentsAt(
650 index, TabStripModel::CLOSE_CREATE_HISTORICAL_TAB);
651 destroyed_watcher.Wait();
652 }
653};
654
655// Tests that as a user switches between tabs, navigates within a tab, and
656// switches between browser windows, the correct URL is being passed to the
657// Handoff.
658IN_PROC_BROWSER_TEST_F(AppControllerHandoffBrowserTest, TestHandoffURLs) {
svaldeza01f7d92015-11-18 17:47:56659 ASSERT_TRUE(embedded_test_server()->Start());
erikchen600f7962014-12-12 00:17:38660 EXPECT_EQ(g_handoff_url, GURL(url::kAboutBlankURL));
661
662 // Test that navigating to a URL updates the handoff URL.
663 GURL test_url1 = embedded_test_server()->GetURL("/title1.html");
664 ui_test_utils::NavigateToURL(browser(), test_url1);
665 EXPECT_EQ(g_handoff_url, test_url1);
666
667 // Test that opening a new tab updates the handoff URL.
668 GURL test_url2 = embedded_test_server()->GetURL("/title2.html");
669 chrome::NavigateParams params(browser(), test_url2, ui::PAGE_TRANSITION_LINK);
nick3b04f322016-08-31 19:29:19670 params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
erikchen600f7962014-12-12 00:17:38671 ui_test_utils::NavigateToURL(&params);
672 EXPECT_EQ(g_handoff_url, test_url2);
673
674 // Test that switching tabs updates the handoff URL.
675 browser()->tab_strip_model()->ActivateTabAt(0, true);
676 EXPECT_EQ(g_handoff_url, test_url1);
677
678 // Test that closing the current tab updates the handoff URL.
679 CloseTab(browser(), 0);
680 EXPECT_EQ(g_handoff_url, test_url2);
681
682 // Test that opening a new browser window updates the handoff URL.
683 GURL test_url3 = embedded_test_server()->GetURL("/title3.html");
684 ui_test_utils::NavigateToURLWithDisposition(
nick3b04f322016-08-31 19:29:19685 browser(), GURL(test_url3), WindowOpenDisposition::NEW_WINDOW,
erikchen600f7962014-12-12 00:17:38686 ui_test_utils::BROWSER_TEST_WAIT_FOR_BROWSER);
687 EXPECT_EQ(g_handoff_url, test_url3);
688
689 // Check that there are exactly 2 browsers.
scottmg0d8e4ab2016-01-28 00:34:55690 BrowserList* active_browser_list = BrowserList::GetInstance();
erikchen600f7962014-12-12 00:17:38691 EXPECT_EQ(2u, active_browser_list->size());
692
erikchen64a6d442015-07-10 19:38:20693 // Close the second browser window (which only has 1 tab left).
erikchen600f7962014-12-12 00:17:38694 Browser* browser2 = active_browser_list->get(1);
erikchen64a6d442015-07-10 19:38:20695 CloseBrowserSynchronously(browser2);
erikchen600f7962014-12-12 00:17:38696 EXPECT_EQ(g_handoff_url, test_url2);
697
698 // The URLs of incognito windows should not be passed to Handoff.
699 GURL test_url4 = embedded_test_server()->GetURL("/simple.html");
700 ui_test_utils::NavigateToURLWithDisposition(
nick3b04f322016-08-31 19:29:19701 browser(), GURL(test_url4), WindowOpenDisposition::OFF_THE_RECORD,
erikchen600f7962014-12-12 00:17:38702 ui_test_utils::BROWSER_TEST_WAIT_FOR_BROWSER);
703 EXPECT_EQ(g_handoff_url, GURL());
704
705 // Open a new tab in the incognito window.
706 EXPECT_EQ(2u, active_browser_list->size());
707 Browser* browser3 = active_browser_list->get(1);
708 ui_test_utils::NavigateToURLWithDisposition(
nick3b04f322016-08-31 19:29:19709 browser3, test_url4, WindowOpenDisposition::NEW_FOREGROUND_TAB,
erikchen600f7962014-12-12 00:17:38710 ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
711 EXPECT_EQ(g_handoff_url, GURL());
712
713 // Navigate the current tab in the incognito window.
thestig53986dc2014-12-16 06:09:18714 ui_test_utils::NavigateToURL(browser3, test_url1);
erikchen600f7962014-12-12 00:17:38715 EXPECT_EQ(g_handoff_url, GURL());
716
717 // Activate the original browser window.
718 Browser* browser1 = active_browser_list->get(0);
719 browser1->window()->Show();
720 EXPECT_EQ(g_handoff_url, test_url2);
721}
722
723} // namespace