diff options
author | dimich@chromium.org <dimich@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-27 17:55:54 +0000 |
---|---|---|
committer | dimich@chromium.org <dimich@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-27 17:55:54 +0000 |
commit | 91484afdf575b8dd68ba92238338085f8758dbcd (patch) | |
tree | 6e1b831f09cf9a910211eb515c91ce320589aae6 | |
parent | d852274be5cd2157e9519c9dc587e29a091032fa (diff) | |
download | chromium_src-91484afdf575b8dd68ba92238338085f8758dbcd.zip chromium_src-91484afdf575b8dd68ba92238338085f8758dbcd.tar.gz chromium_src-91484afdf575b8dd68ba92238338085f8758dbcd.tar.bz2 |
Re-land. Fixed previous errors.
Implement OSX version for "Start on Login" for background-enabled extensions.
When user installs first background-enabled extension we set Chrome to be a hidden "Login Item", and when user uninstalls the last one, we remove Chrome from the "Login Items List".
Login Items are the apps that are listed in Systempreferences->Accounts->Login Items, or which have a "Open on Login" check for their Dock icon.
Since we manipulate the user-faced UI item here, we try to do it in a way that will likely not interfere with user settings. Most typically, users do not use 'hidden' property, so if we ever encounter setting w/o this property, we won't override. Also there is a hidden preference used to remember the fact that Chrome changed the settings earlier - to avoid override on uninstall of the last persistent app in opposite case.
Using the same bit that user can manipulate separately and perhaps even knows about is beneficial since it provides for unified place to "start Chrome on Login" and reduces confusion.
Implementation for Login Items List access uses LSSharedFileList API that is available starting 10.5, which I think is our minimal supported version of OSX.
BUG=43382
TEST=none
Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=57569
Reverted
Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=57614
Reverted
Review URL: http://codereview.chromium.org/3163029
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@57699 0039d316-1c4b-4281-b951-d872f2087c98
-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 | 9 | ||||
-rw-r--r-- | chrome/common/pref_names.cc | 5 | ||||
-rw-r--r-- | chrome/common/pref_names.h | 1 |
7 files changed, 240 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..a00c47b 100644 --- a/chrome/browser/browser_main_mac.mm +++ b/chrome/browser/browser_main_mac.mm @@ -68,6 +68,15 @@ class BrowserMainPartsMac : public BrowserMainPartsPosix { : BrowserMainPartsPosix(parameters) {} protected: + virtual void PreEarlyInitialization() { + BrowserMainPartsPosix::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 2ce27d1..c8402a3 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc @@ -694,6 +694,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 885cb67..0ff87c6 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -252,6 +252,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[]; |