1 // Copyright (c) 2011 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 #include "base/bind.h" 6 #include "base/command_line.h" 7 #include "base/mac/mac_util.h" 8 #include "base/prefs/pref_service.h" 9 #include "chrome/browser/background/background_mode_manager.h" 10 #include "chrome/browser/browser_process.h" 11 #include "chrome/common/chrome_switches.h" 12 #include "chrome/common/pref_names.h" 13 #include "content/public/browser/browser_thread.h" 14 #include "grit/generated_resources.h" 15 #include "ui/base/l10n/l10n_util.h" 16 17 using content::BrowserThread; 18 19 namespace { 20 void SetUserRemovedLoginItemPrefOnUIThread() { 21 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 22 PrefService* service = g_browser_process->local_state(); 23 service->SetBoolean(prefs::kUserRemovedLoginItem, true); 24 } 25 26 void SetCreatedLoginItemPrefOnUIThread() { 27 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 28 PrefService* service = g_browser_process->local_state(); 29 service->SetBoolean(prefs::kChromeCreatedLoginItem, true); 30 } 31 32 void DisableLaunchOnStartupOnFileThread() { 33 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); 34 // If the LoginItem is not hidden, it means it's user created, so don't 35 // delete it. 36 bool is_hidden = false; 37 if (base::mac::CheckLoginItemStatus(&is_hidden) && is_hidden) 38 base::mac::RemoveFromLoginItems(); 39 } 40 41 void CheckForUserRemovedLoginItemOnFileThread() { 42 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); 43 if (!base::mac::CheckLoginItemStatus(NULL)) { 44 // There's no LoginItem, so set the kUserRemovedLoginItem pref. 45 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 46 base::Bind(SetUserRemovedLoginItemPrefOnUIThread)); 47 } 48 } 49 50 void EnableLaunchOnStartupOnFileThread(bool need_migration) { 51 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); 52 if (need_migration) { 53 // This is the first time running Chrome since the kChromeCreatedLoginItem 54 // pref was added. Initialize the status of this pref based on whether 55 // there is already a hidden login item. 56 bool is_hidden = false; 57 if (base::mac::CheckLoginItemStatus(&is_hidden)) { 58 if (is_hidden) { 59 // We already have a hidden login item, so set the kChromeCreatedLoginItem 60 // flag. 61 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 62 base::Bind(SetCreatedLoginItemPrefOnUIThread)); 63 } 64 // LoginItem already exists - just exit. 65 return; 66 } 67 } 68 69 // Check if Chrome is already a Login Item - if not, create one. 70 if (!base::mac::CheckLoginItemStatus(NULL)) { 71 // Call back to the UI thread to set our preference so we know that Chrome 72 // created the login item (which means we are allowed to delete it later). 73 // There's a race condition here if the user disables launch on startup 74 // before our callback is run, but the user can manually disable 75 // "Open At Login" via the dock if this happens. 76 base::mac::AddToLoginItems(true); // Hide on startup. 77 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 78 base::Bind(SetCreatedLoginItemPrefOnUIThread)); 79 } 80 } 81 82 } // namespace 83 84 void BackgroundModeManager::EnableLaunchOnStartup(bool should_launch) { 85 // LoginItems are associated with an executable, not with a specific 86 // user-data-dir, so only mess with the LoginItem when running with the 87 // default user-data-dir. So if a user is running multiple instances of 88 // Chrome with different user-data-dirs, they won't conflict in their 89 // use of LoginItems. 90 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUserDataDir)) 91 return; 92 93 // There are a few cases we need to handle: 94 // 95 // 1) Chrome is transitioning to "launch on startup" state, and there's no 96 // login item currently. We create a new item if the kUserRemovedLoginItem 97 // and kChromeCreatedLoginItem flags are already false, and set the 98 // kChromeCreatedLoginItem flag to true. If kChromeCreatedLoginItem is 99 // already set (meaning that we created a login item that has since been 100 // deleted) then we will set the kUserRemovedLoginItem so we do not create 101 // login items in the future. 102 // 103 // 2) Chrome is transitioning to the "do not launch on startup" state. If 104 // the kChromeCreatedLoginItem flag is false, we do nothing. Otherwise, we 105 // will delete the login item if it's present, and not we will set 106 // kUserRemovedLoginItem to true to prevent future login items from being 107 // created. 108 if (should_launch) { 109 PrefService* service = g_browser_process->local_state(); 110 // If the user removed the login item, don't ever create another one. 111 if (service->GetBoolean(prefs::kUserRemovedLoginItem)) 112 return; 113 114 if (service->GetBoolean(prefs::kChromeCreatedLoginItem)) { 115 DCHECK(service->GetBoolean(prefs::kMigratedLoginItemPref)); 116 // If we previously created a login item, we don't need to create 117 // a new one - just check to see if the user removed it so we don't 118 // ever create another one. 119 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 120 base::Bind( 121 CheckForUserRemovedLoginItemOnFileThread)); 122 } else { 123 bool need_migration = !service->GetBoolean( 124 prefs::kMigratedLoginItemPref); 125 service->SetBoolean(prefs::kMigratedLoginItemPref, true); 126 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 127 base::Bind(EnableLaunchOnStartupOnFileThread, 128 need_migration)); 129 } 130 } else { 131 PrefService* service = g_browser_process->local_state(); 132 // If Chrome didn't create any login items, just exit. 133 if (!service->GetBoolean(prefs::kChromeCreatedLoginItem)) 134 return; 135 136 // Clear the pref now that we're removing the login item. 137 service->ClearPref(prefs::kChromeCreatedLoginItem); 138 139 // If the user removed our login item, note this so we don't ever create 140 // another one. 141 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 142 base::Bind( 143 CheckForUserRemovedLoginItemOnFileThread)); 144 145 // Call to the File thread to remove the login item since it requires 146 // accessing the disk. 147 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 148 base::Bind(DisableLaunchOnStartupOnFileThread)); 149 } 150 } 151 152 void BackgroundModeManager::DisplayAppInstalledNotification( 153 const extensions::Extension* extension) { 154 // TODO(atwilson): Display a platform-appropriate notification here. 155 // http://crbug.com/74970 156 } 157 158 base::string16 BackgroundModeManager::GetPreferencesMenuLabel() { 159 return l10n_util::GetStringUTF16(IDS_OPTIONS); 160 } 161