auto_launch_util.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1// 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 5#include "chrome/installer/util/auto_launch_util.h" 6 7#include "base/command_line.h" 8#include "base/files/file_path.h" 9#include "base/logging.h" 10#include "base/path_service.h" 11#include "base/strings/string_number_conversions.h" 12#include "base/strings/utf_string_conversions.h" 13#include "base/win/win_util.h" 14#include "chrome/common/chrome_constants.h" 15#include "chrome/common/chrome_switches.h" 16#include "chrome/common/chrome_version_info.h" 17#include "chrome/installer/util/browser_distribution.h" 18#include "chrome/installer/util/product.h" 19#include "chrome/installer/util/util_constants.h" 20#include "crypto/sha2.h" 21 22using base::ASCIIToUTF16; 23using base::ASCIIToWide; 24 25namespace auto_launch_util { 26 27// The prefix of the Chrome Auto-launch key under the Run key. 28const wchar_t kAutolaunchKeyValue[] = L"GoogleChromeAutoLaunch"; 29 30// We use one Run key with flags specifying which feature we want to start up. 31// When we change our Run key we need to specify what we want to do with each 32// flag. This lists the possible actions we can take with the flags. 33enum FlagSetting { 34 FLAG_DISABLE, // Disable the flag. 35 FLAG_ENABLE, // Enable the flag. 36 FLAG_PRESERVE, // Preserve the value that the flag has currently. 37}; 38 39// A helper function that takes a |profile_path| and builds a registry key 40// name to use when deciding where to read/write the auto-launch value 41// to/from. It takes into account the name of the profile (so that different 42// installations of Chrome don't conflict, and so the in the future different 43// profiles can be auto-launched (or not) separately). 44base::string16 ProfileToKeyName(const base::string16& profile_directory) { 45 base::FilePath path; 46 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 47 if (command_line.HasSwitch(switches::kUserDataDir)) { 48 path = command_line.GetSwitchValuePath(switches::kUserDataDir); 49 } else { 50 // Get the path from the same source as the installer, to make sure there 51 // are no differences. 52 BrowserDistribution* distribution = 53 BrowserDistribution::GetSpecificDistribution( 54 BrowserDistribution::CHROME_BROWSER); 55 installer::Product product(distribution); 56 std::vector<base::FilePath> data_dir_paths; 57 product.GetUserDataPaths(&data_dir_paths); 58 if (!data_dir_paths.empty()) 59 path = data_dir_paths[0]; 60 } 61 path = path.Append(profile_directory); 62 63 std::string input(path.AsUTF8Unsafe()); 64 uint8 hash[16]; 65 crypto::SHA256HashString(input, hash, sizeof(hash)); 66 std::string hash_string = base::HexEncode(hash, sizeof(hash)); 67 return base::string16(kAutolaunchKeyValue) + ASCIIToWide("_") + 68 ASCIIToWide(hash_string); 69} 70 71// Returns whether the Chrome executable specified in |application_path| is set 72// to auto-launch at computer startup with a given |command_line_switch|. 73// NOTE: |application_path| is optional and should be blank in most cases (as 74// it will default to the application path of the current executable). 75// |profile_directory| is the name of the directory (leaf, not the full path) 76// that contains the profile that should be opened at computer startup. 77// |command_line_switch| is the switch we are optionally interested in and, if 78// not blank, must be present for the function to return true. If blank, it acts 79// like a wildcard. 80bool WillLaunchAtLoginWithSwitch(const base::FilePath& application_path, 81 const base::string16& profile_directory, 82 const std::string& command_line_switch) { 83 base::string16 key_name(ProfileToKeyName(profile_directory)); 84 base::string16 autolaunch; 85 if (!base::win::ReadCommandFromAutoRun( 86 HKEY_CURRENT_USER, key_name, &autolaunch)) { 87 return false; 88 } 89 90 base::FilePath chrome_exe(application_path); 91 if (chrome_exe.empty()) { 92 if (!PathService::Get(base::DIR_EXE, &chrome_exe)) { 93 NOTREACHED(); 94 return false; 95 } 96 } 97 chrome_exe = chrome_exe.Append(installer::kChromeExe); 98 99 if (autolaunch.find(chrome_exe.value()) == base::string16::npos) 100 return false; 101 102 return command_line_switch.empty() || 103 autolaunch.find(ASCIIToUTF16(command_line_switch)) != 104 base::string16::npos; 105} 106 107bool AutoStartRequested(const base::string16& profile_directory, 108 bool window_requested, 109 const base::FilePath& application_path) { 110 if (window_requested) { 111 return WillLaunchAtLoginWithSwitch(application_path, 112 profile_directory, 113 switches::kAutoLaunchAtStartup); 114 } else { 115 // Background mode isn't profile specific, but is attached to the Run key 116 // for the Default profile. 117 return WillLaunchAtLoginWithSwitch(application_path, 118 ASCIIToUTF16(chrome::kInitialProfile), 119 switches::kNoStartupWindow); 120 } 121} 122 123bool CheckAndRemoveDeprecatedBackgroundModeSwitch() { 124 // For backwards compatibility we need to provide a migration path from the 125 // previously used key "chromium" that the BackgroundMode used to set, as it 126 // is incompatible with the new key (can't have two Run keys with 127 // conflicting switches). 128 base::string16 chromium = ASCIIToUTF16("chromium"); 129 base::string16 value; 130 if (base::win::ReadCommandFromAutoRun(HKEY_CURRENT_USER, chromium, &value)) { 131 if (value.find(ASCIIToUTF16(switches::kNoStartupWindow)) != 132 base::string16::npos) { 133 base::win::RemoveCommandFromAutoRun(HKEY_CURRENT_USER, chromium); 134 return true; 135 } 136 } 137 138 return false; 139} 140 141void SetWillLaunchAtLogin(const base::FilePath& application_path, 142 const base::string16& profile_directory, 143 FlagSetting foreground_mode, 144 FlagSetting background_mode) { 145 if (CheckAndRemoveDeprecatedBackgroundModeSwitch()) { 146 // We've found the deprecated switch, we must migrate it (unless background 147 // mode is being turned off). 148 if (profile_directory == ASCIIToUTF16(chrome::kInitialProfile) && 149 background_mode == FLAG_PRESERVE) { 150 // Preserve in this case also covers the deprecated value, so we must 151 // explicitly turn the flag on and the rest will be taken care of below. 152 background_mode = FLAG_ENABLE; 153 } else { 154 // When we add support for multiple profiles for foreground mode we need 155 // to think about where to store the background mode switch. I think we 156 // need to store it with the Default profile (call SetWillLaunchAtLogin 157 // again specifying the Default profile), but concerns were raised in 158 // review. 159 NOTREACHED(); 160 } 161 } 162 base::string16 key_name(ProfileToKeyName(profile_directory)); 163 164 // Check which feature should be enabled. 165 bool in_foreground = 166 foreground_mode == FLAG_ENABLE || 167 (foreground_mode == FLAG_PRESERVE && 168 WillLaunchAtLoginWithSwitch(application_path, 169 profile_directory, 170 switches::kAutoLaunchAtStartup)); 171 bool in_background = 172 background_mode == FLAG_ENABLE || 173 (background_mode == FLAG_PRESERVE && 174 WillLaunchAtLoginWithSwitch(application_path, 175 profile_directory, 176 switches::kNoStartupWindow)); 177 178 // TODO(finnur): Convert this into a shortcut, instead of using the Run key. 179 if (in_foreground || in_background) { 180 base::FilePath path(application_path); 181 if (path.empty()) { 182 if (!PathService::Get(base::DIR_EXE, &path)) { 183 NOTREACHED(); 184 return; 185 } 186 } 187 base::string16 cmd_line = ASCIIToUTF16("\""); 188 cmd_line += path.value(); 189 cmd_line += ASCIIToUTF16("\\"); 190 cmd_line += installer::kChromeExe; 191 cmd_line += ASCIIToUTF16("\""); 192 193 if (in_background) { 194 cmd_line += ASCIIToUTF16(" --"); 195 cmd_line += ASCIIToUTF16(switches::kNoStartupWindow); 196 } 197 if (in_foreground) { 198 cmd_line += ASCIIToUTF16(" --"); 199 cmd_line += ASCIIToUTF16(switches::kAutoLaunchAtStartup); 200 201 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 202 if (command_line.HasSwitch(switches::kUserDataDir)) { 203 cmd_line += ASCIIToUTF16(" --"); 204 cmd_line += ASCIIToUTF16(switches::kUserDataDir); 205 cmd_line += ASCIIToUTF16("=\""); 206 cmd_line += 207 command_line.GetSwitchValuePath(switches::kUserDataDir).value(); 208 cmd_line += ASCIIToUTF16("\""); 209 } 210 211 cmd_line += ASCIIToUTF16(" --"); 212 cmd_line += ASCIIToUTF16(switches::kProfileDirectory); 213 cmd_line += ASCIIToUTF16("=\""); 214 cmd_line += profile_directory; 215 cmd_line += ASCIIToUTF16("\""); 216 } 217 218 base::win::AddCommandToAutoRun( 219 HKEY_CURRENT_USER, key_name, cmd_line); 220 } else { 221 base::win::RemoveCommandFromAutoRun(HKEY_CURRENT_USER, key_name); 222 } 223} 224 225void DisableAllAutoStartFeatures(const base::string16& profile_directory) { 226 DisableForegroundStartAtLogin(profile_directory); 227 DisableBackgroundStartAtLogin(); 228} 229 230void EnableForegroundStartAtLogin(const base::string16& profile_directory, 231 const base::FilePath& application_path) { 232 SetWillLaunchAtLogin( 233 application_path, profile_directory, FLAG_ENABLE, FLAG_PRESERVE); 234} 235 236void DisableForegroundStartAtLogin(const base::string16& profile_directory) { 237 SetWillLaunchAtLogin( 238 base::FilePath(), profile_directory, FLAG_DISABLE, FLAG_PRESERVE); 239} 240 241void EnableBackgroundStartAtLogin() { 242 // Background mode isn't profile specific, but we specify the Default profile 243 // just to have a unique Run key to attach it to. FilePath is blank because 244 // this function is not called from the installer (see comments for 245 // EnableAutoStartAtLogin). 246 SetWillLaunchAtLogin(base::FilePath(), 247 ASCIIToUTF16(chrome::kInitialProfile), 248 FLAG_PRESERVE, 249 FLAG_ENABLE); 250} 251 252void DisableBackgroundStartAtLogin() { 253 SetWillLaunchAtLogin(base::FilePath(), 254 ASCIIToUTF16(chrome::kInitialProfile), 255 FLAG_PRESERVE, 256 FLAG_DISABLE); 257} 258 259} // namespace auto_launch_util 260