diff options
-rw-r--r-- | base/mac_util.h | 20 | ||||
-rw-r--r-- | base/mac_util.mm | 149 | ||||
-rw-r--r-- | chrome/browser/background_mode_manager.cc | 51 | ||||
-rw-r--r-- | chrome/browser/background_mode_manager.h | 6 | ||||
-rw-r--r-- | chrome/browser/browser_main_mac.mm | 7 | ||||
-rw-r--r-- | chrome/common/pref_names.cc | 5 | ||||
-rw-r--r-- | chrome/common/pref_names.h | 1 |
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[]; |