summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/mac_util.h20
-rw-r--r--base/mac_util.mm149
-rw-r--r--chrome/browser/background_mode_manager.cc51
-rw-r--r--chrome/browser/background_mode_manager.h6
-rw-r--r--chrome/browser/browser_main_mac.mm7
-rw-r--r--chrome/common/pref_names.cc5
-rw-r--r--chrome/common/pref_names.h1
7 files changed, 238 insertions, 1 deletions
diff --git a/base/mac_util.h b/base/mac_util.h
index d50e9e3..4b8d636 100644
--- a/base/mac_util.h
+++ b/base/mac_util.h
@@ -149,6 +149,26 @@ void SetProcessName(CFStringRef process_name);
// releasing the return value.
CGImageRef CopyNSImageToCGImage(NSImage* image);
+// Checks if the current application is set as a Login Item, so it will launch
+// on Login. If a non-NULL pointer to is_hidden is passed, the Login Item also
+// is queried for the 'hide on launch' flag.
+bool CheckLoginItemStatus(bool* is_hidden);
+
+// Adds current application to the set of Login Items with specified "hide"
+// flag. This has the same effect as adding/removing the application in
+// SystemPreferences->Accounts->LoginItems or marking Application in the Dock
+// as "Options->Open on Login".
+// Does nothing if the application is already set up as Login Item with
+// specified hide flag.
+void AddToLoginItems(bool hide_on_startup);
+
+// Removes the current application from the list Of Login Items.
+void RemoveFromLoginItems();
+
+// Returns true if the current process was automatically launched as a
+// 'Login Item' with 'hide on startup' flag. Used to suppress opening windows.
+bool WasLaunchedAsHiddenLoginItem();
+
} // namespace mac_util
#endif // BASE_MAC_UTIL_H_
diff --git a/base/mac_util.mm b/base/mac_util.mm
index 8b0d481..fa25501 100644
--- a/base/mac_util.mm
+++ b/base/mac_util.mm
@@ -10,6 +10,7 @@
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/scoped_cftyperef.h"
+#include "base/scoped_nsobject.h"
#include "base/sys_string_conversions.h"
namespace {
@@ -49,6 +50,78 @@ void SetUIMode() {
SetSystemUIMode(desired_mode, desired_options);
}
+bool WasLaunchedAsLoginItem() {
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+
+ scoped_nsobject<NSDictionary> process_info(
+ reinterpret_cast<const NSDictionary*>(
+ ProcessInformationCopyDictionary(&psn,
+ kProcessDictionaryIncludeAllInformationMask)));
+
+ long long temp = [[process_info objectForKey:@"ParentPSN"] longLongValue];
+ ProcessSerialNumber parent_psn =
+ { (temp >> 32) & 0x00000000FFFFFFFFLL, temp & 0x00000000FFFFFFFFLL };
+
+ scoped_nsobject<NSDictionary> parent_info(
+ reinterpret_cast<const NSDictionary*>(
+ ProcessInformationCopyDictionary(&parent_psn,
+ kProcessDictionaryIncludeAllInformationMask)));
+
+ // Check that creator process code is that of loginwindow.
+ BOOL result =
+ [[parent_info objectForKey:@"FileCreator"] isEqualToString:@"lgnw"];
+
+ return result == YES;
+}
+
+// Looks into Shared File Lists corresponding to Login Items for the item
+// representing the current application. If such an item is found, returns
+// retained reference to it. Caller is responsible for releasing the reference.
+LSSharedFileListItemRef GetLoginItemForApp() {
+ scoped_cftyperef<LSSharedFileListRef> login_items(LSSharedFileListCreate(
+ NULL, kLSSharedFileListSessionLoginItems, NULL));
+
+ if (!login_items.get()) {
+ LOG(ERROR) << "Couldn't get a Login Items list.";
+ return NULL;
+ }
+
+ scoped_nsobject<NSArray> login_items_array(reinterpret_cast<const NSArray*>(
+ LSSharedFileListCopySnapshot(login_items, NULL)));
+
+ NSURL* url = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
+
+ for(NSUInteger i = 0; i < [login_items_array count]; ++i) {
+ LSSharedFileListItemRef item = reinterpret_cast<LSSharedFileListItemRef>(
+ [login_items_array objectAtIndex:i]);
+ CFURLRef item_url_ref = NULL;
+
+ if (LSSharedFileListItemResolve(item, 0, &item_url_ref, NULL) == noErr) {
+ scoped_cftyperef<CFURLRef> item_url(item_url_ref);
+ if (CFEqual(item_url, url)) {
+ CFRetain(item);
+ return item;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+// kLSSharedFileListLoginItemHidden is supported on
+// 10.5, but missing from the 10.5 headers.
+// http://openradar.appspot.com/6482251
+static NSString* kLSSharedFileListLoginItemHidden =
+ @"com.apple.loginitem.HideOnLaunch";
+
+bool IsHiddenLoginItem(LSSharedFileListItemRef item) {
+ scoped_cftyperef<CFBooleanRef> hidden(reinterpret_cast<CFBooleanRef>(
+ LSSharedFileListItemCopyProperty(item,
+ reinterpret_cast<CFStringRef>(kLSSharedFileListLoginItemHidden))));
+
+ return hidden && hidden == kCFBooleanTrue;
+}
+
} // end namespace
namespace mac_util {
@@ -515,4 +588,80 @@ CGImageRef CopyNSImageToCGImage(NSImage* image) {
return CGBitmapContextCreateImage(context);
}
+bool CheckLoginItemStatus(bool* is_hidden) {
+ scoped_cftyperef<LSSharedFileListItemRef> item(GetLoginItemForApp());
+ if (!item.get())
+ return false;
+
+ if (is_hidden)
+ *is_hidden = IsHiddenLoginItem(item);
+
+ return true;
+}
+
+void AddToLoginItems(bool hide_on_startup) {
+ scoped_cftyperef<LSSharedFileListItemRef> item(GetLoginItemForApp());
+ if (item.get() && (IsHiddenLoginItem(item) == hide_on_startup)) {
+ return; // Already is a login item with required hide flag.
+ }
+
+ scoped_cftyperef<LSSharedFileListRef> login_items(LSSharedFileListCreate(
+ NULL, kLSSharedFileListSessionLoginItems, NULL));
+
+ if (!login_items.get()) {
+ LOG(ERROR) << "Couldn't get a Login Items list.";
+ return;
+ }
+
+ // Remove the old item, it has wrong hide flag, we'll create a new one.
+ if (item.get()) {
+ LSSharedFileListItemRemove(login_items, item);
+ }
+
+ NSURL* url = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
+
+ BOOL hide = hide_on_startup ? YES : NO;
+ NSDictionary* properties =
+ [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:hide]
+ forKey:kLSSharedFileListLoginItemHidden];
+
+ scoped_cftyperef<LSSharedFileListItemRef> new_item;
+ new_item.reset(LSSharedFileListInsertItemURL(
+ login_items, kLSSharedFileListItemLast, NULL, NULL,
+ reinterpret_cast<CFURLRef>(url),
+ reinterpret_cast<CFDictionaryRef>(properties), NULL));
+
+ if (!new_item.get()) {
+ LOG(ERROR) << "Couldn't insert current app into Login Items list.";
+ }
+}
+
+void RemoveFromLoginItems() {
+ scoped_cftyperef<LSSharedFileListItemRef> item(GetLoginItemForApp());
+ if (!item.get())
+ return;
+
+ scoped_cftyperef<LSSharedFileListRef> login_items(LSSharedFileListCreate(
+ NULL, kLSSharedFileListSessionLoginItems, NULL));
+
+ if (!login_items.get()) {
+ LOG(ERROR) << "Couldn't get a Login Items list.";
+ return;
+ }
+
+ LSSharedFileListItemRemove(login_items, item);
+}
+
+bool WasLaunchedAsHiddenLoginItem() {
+ if (!WasLaunchedAsLoginItem())
+ return false;
+
+ scoped_cftyperef<LSSharedFileListItemRef> item(GetLoginItemForApp());
+ if (!item.get()) {
+ LOG(ERROR) << "Process launched at Login but can't access Login Item List.";
+ return false;
+ }
+ return IsHiddenLoginItem(item);
+}
+
} // namespace mac_util
diff --git a/chrome/browser/background_mode_manager.cc b/chrome/browser/background_mode_manager.cc
index 871e243..b619177 100644
--- a/chrome/browser/background_mode_manager.cc
+++ b/chrome/browser/background_mode_manager.cc
@@ -23,6 +23,10 @@
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
+#if defined(OS_MACOSX)
+#include "base/mac_util.h"
+#endif
+
#if defined(TOOLKIT_GTK)
#include "chrome/browser/gtk/gtk_util.h"
#endif
@@ -82,13 +86,27 @@ bool BackgroundModeManager::IsBackgroundModeEnabled() {
return profile_->GetPrefs()->GetBoolean(prefs::kBackgroundModeEnabled);
}
+bool BackgroundModeManager::IsLaunchOnStartupResetAllowed() {
+ return profile_->GetPrefs()->GetBoolean(prefs::kLaunchOnStartupResetAllowed);
+}
+
+void BackgroundModeManager::SetLaunchOnStartupResetAllowed(bool allowed) {
+ profile_->GetPrefs()->SetBoolean(prefs::kLaunchOnStartupResetAllowed,
+ allowed);
+}
+
void BackgroundModeManager::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
switch (type.value) {
case NotificationType::EXTENSIONS_READY:
+ // On a Mac, we use 'login items' mechanism which has user-facing UI so we
+ // don't want to stomp on user choice every time we start and load
+ // registered extensions.
+#if !defined(OS_MACOSX)
EnableLaunchOnStartup(IsBackgroundModeEnabled() &&
background_app_count_ > 0);
+#endif
break;
case NotificationType::EXTENSION_LOADED:
if (IsBackgroundApp(Details<Extension>(details).ptr()))
@@ -171,8 +189,38 @@ void BackgroundModeManager::OnBackgroundAppUninstalled() {
}
void BackgroundModeManager::EnableLaunchOnStartup(bool should_launch) {
- // TODO(atwilson): Add platform-specific code to enable/disable launch on
+ // TODO(BUG43382): Add code for other platforms to enable/disable launch on
// startup.
+
+ // This functionality is only defined for default profile, currently.
+ if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUserDataDir))
+ return;
+
+#if defined(OS_MACOSX)
+ if (should_launch) {
+ // Return if Chrome is already a Login Item (avoid overriding user choice).
+ if (mac_util::CheckLoginItemStatus(NULL))
+ return;
+
+ mac_util::AddToLoginItems(true); // Hide on startup.
+
+ // Remember we set Login Item, not the user - so we can reset it later.
+ SetLaunchOnStartupResetAllowed(true);
+ } else {
+ // If we didn't set Login Item, don't mess with it.
+ if (!IsLaunchOnStartupResetAllowed())
+ return;
+ SetLaunchOnStartupResetAllowed(false);
+
+ // Check if Chrome is not a login Item, or is a Login Item but w/o 'hidden'
+ // flag - most likely user has modified the setting, don't override it.
+ bool is_hidden = false;
+ if (!mac_util::CheckLoginItemStatus(&is_hidden) || !is_hidden)
+ return;
+
+ mac_util::RemoveFromLoginItems();
+ }
+#endif
}
void BackgroundModeManager::CreateStatusTrayIcon() {
@@ -269,4 +317,5 @@ Browser* BackgroundModeManager::GetBrowserWindow() {
// static
void BackgroundModeManager::RegisterUserPrefs(PrefService* prefs) {
prefs->RegisterBooleanPref(prefs::kBackgroundModeEnabled, true);
+ prefs->RegisterBooleanPref(prefs::kLaunchOnStartupResetAllowed, false);
}
diff --git a/chrome/browser/background_mode_manager.h b/chrome/browser/background_mode_manager.h
index 3a82399..38b010c 100644
--- a/chrome/browser/background_mode_manager.h
+++ b/chrome/browser/background_mode_manager.h
@@ -82,6 +82,12 @@ class BackgroundModeManager
// Returns true if the background mode preference is enabled
bool IsBackgroundModeEnabled();
+ // Returns true if chrome has set "launch on startup" property for itself
+ // earlier and is allowed to reset it later, reducing likelihood of
+ // overriding user choices.
+ bool IsLaunchOnStartupResetAllowed();
+ void SetLaunchOnStartupResetAllowed(bool allowed);
+
// Called to make sure that our launch-on-startup mode is properly set.
// (virtual so we can override for tests).
virtual void EnableLaunchOnStartup(bool should_launch);
diff --git a/chrome/browser/browser_main_mac.mm b/chrome/browser/browser_main_mac.mm
index 5aa5999..3f4017d 100644
--- a/chrome/browser/browser_main_mac.mm
+++ b/chrome/browser/browser_main_mac.mm
@@ -68,6 +68,13 @@ class BrowserMainPartsMac : public BrowserMainPartsPosix {
: BrowserMainPartsPosix(parameters) {}
protected:
+ virtual void PreEarlyInitialization() {
+ if (mac_util::WasLaunchedAsHiddenLoginItem()) {
+ CommandLine* singleton_command_line = CommandLine::ForCurrentProcess();
+ singleton_command_line->AppendSwitch(switches::kNoStartupWindow);
+ }
+ }
+
virtual void PreMainMessageLoopStart() {
BrowserMainPartsPosix::PreMainMessageLoopStart();
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index cf74af4..72820ef 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -691,6 +691,11 @@ const char kMemoryCacheSize[] = "renderer.memory_cache.size";
// apps are installed.
const char kBackgroundModeEnabled[] = "background_mode.enabled";
+// Boolean that records if chrome has set "launch on startup" property for
+// itself earlier and is allowed to reset it later, reducing likelihood of
+// overriding user choices.
+const char kLaunchOnStartupResetAllowed[] = "launch_on_startup_reset_allowed";
+
// String which specifies where to download files to by default.
const char kDownloadDefaultDirectory[] = "download.default_directory";
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 232d0fd..714def0 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -251,6 +251,7 @@ extern const char kPreferencesWindowPlacement[];
extern const char kMemoryCacheSize[];
extern const char kBackgroundModeEnabled[];
+extern const char kLaunchOnStartupResetAllowed[];
extern const char kDownloadDefaultDirectory[];
extern const char kDownloadExtensionsToOpen[];