auto_launch_util.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
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 path = product.GetUserDataPath(); 57 } 58 path = path.Append(profile_directory); 59 60 std::string input(path.AsUTF8Unsafe()); 61 uint8 hash[16]; 62 crypto::SHA256HashString(input, hash, sizeof(hash)); 63 std::string hash_string = base::HexEncode(hash, sizeof(hash)); 64 return base::string16(kAutolaunchKeyValue) + ASCIIToWide("_") + 65 ASCIIToWide(hash_string); 66} 67 68// Returns whether the Chrome executable specified in |application_path| is set 69// to auto-launch at computer startup with a given |command_line_switch|. 70// NOTE: |application_path| is optional and should be blank in most cases (as 71// it will default to the application path of the current executable). 72// |profile_directory| is the name of the directory (leaf, not the full path) 73// that contains the profile that should be opened at computer startup. 74// |command_line_switch| is the switch we are optionally interested in and, if 75// not blank, must be present for the function to return true. If blank, it acts 76// like a wildcard. 77bool WillLaunchAtLoginWithSwitch(const base::FilePath& application_path, 78 const base::string16& profile_directory, 79 const std::string& command_line_switch) { 80 base::string16 key_name(ProfileToKeyName(profile_directory)); 81 base::string16 autolaunch; 82 if (!base::win::ReadCommandFromAutoRun( 83 HKEY_CURRENT_USER, key_name, &autolaunch)) { 84 return false; 85 } 86 87 base::FilePath chrome_exe(application_path); 88 if (chrome_exe.empty()) { 89 if (!PathService::Get(base::DIR_EXE, &chrome_exe)) { 90 NOTREACHED(); 91 return false; 92 } 93 } 94 chrome_exe = chrome_exe.Append(installer::kChromeExe); 95 96 if (autolaunch.find(chrome_exe.value()) == base::string16::npos) 97 return false; 98 99 return command_line_switch.empty() || 100 autolaunch.find(ASCIIToUTF16(command_line_switch)) != 101 base::string16::npos; 102} 103 104bool AutoStartRequested(const base::string16& profile_directory, 105 bool window_requested, 106 const base::FilePath& application_path) { 107 if (window_requested) { 108 return WillLaunchAtLoginWithSwitch(application_path, 109 profile_directory, 110 switches::kAutoLaunchAtStartup); 111 } else { 112 // Background mode isn't profile specific, but is attached to the Run key 113 // for the Default profile. 114 return WillLaunchAtLoginWithSwitch(application_path, 115 ASCIIToUTF16(chrome::kInitialProfile), 116 switches::kNoStartupWindow); 117 } 118} 119 120bool CheckAndRemoveDeprecatedBackgroundModeSwitch() { 121 // For backwards compatibility we need to provide a migration path from the 122 // previously used key "chromium" that the BackgroundMode used to set, as it 123 // is incompatible with the new key (can't have two Run keys with 124 // conflicting switches). 125 base::string16 chromium = ASCIIToUTF16("chromium"); 126 base::string16 value; 127 if (base::win::ReadCommandFromAutoRun(HKEY_CURRENT_USER, chromium, &value)) { 128 if (value.find(ASCIIToUTF16(switches::kNoStartupWindow)) != 129 base::string16::npos) { 130 base::win::RemoveCommandFromAutoRun(HKEY_CURRENT_USER, chromium); 131 return true; 132 } 133 } 134 135 return false; 136} 137 138void SetWillLaunchAtLogin(const base::FilePath& application_path, 139 const base::string16& profile_directory, 140 FlagSetting foreground_mode, 141 FlagSetting background_mode) { 142 if (CheckAndRemoveDeprecatedBackgroundModeSwitch()) { 143 // We've found the deprecated switch, we must migrate it (unless background 144 // mode is being turned off). 145 if (profile_directory == ASCIIToUTF16(chrome::kInitialProfile) && 146 background_mode == FLAG_PRESERVE) { 147 // Preserve in this case also covers the deprecated value, so we must 148 // explicitly turn the flag on and the rest will be taken care of below. 149 background_mode = FLAG_ENABLE; 150 } else { 151 // When we add support for multiple profiles for foreground mode we need 152 // to think about where to store the background mode switch. I think we 153 // need to store it with the Default profile (call SetWillLaunchAtLogin 154 // again specifying the Default profile), but concerns were raised in 155 // review. 156 NOTREACHED(); 157 } 158 } 159 base::string16 key_name(ProfileToKeyName(profile_directory)); 160 161 // Check which feature should be enabled. 162 bool in_foreground = 163 foreground_mode == FLAG_ENABLE || 164 (foreground_mode == FLAG_PRESERVE && 165 WillLaunchAtLoginWithSwitch(application_path, 166 profile_directory, 167 switches::kAutoLaunchAtStartup)); 168 bool in_background = 169 background_mode == FLAG_ENABLE || 170 (background_mode == FLAG_PRESERVE && 171 WillLaunchAtLoginWithSwitch(application_path, 172 profile_directory, 173 switches::kNoStartupWindow)); 174 175 // TODO(finnur): Convert this into a shortcut, instead of using the Run key. 176 if (in_foreground || in_background) { 177 base::FilePath path(application_path); 178 if (path.empty()) { 179 if (!PathService::Get(base::DIR_EXE, &path)) { 180 NOTREACHED(); 181 return; 182 } 183 } 184 base::string16 cmd_line = ASCIIToUTF16("\""); 185 cmd_line += path.value(); 186 cmd_line += ASCIIToUTF16("\\"); 187 cmd_line += installer::kChromeExe; 188 cmd_line += ASCIIToUTF16("\""); 189 190 if (in_background) { 191 cmd_line += ASCIIToUTF16(" --"); 192 cmd_line += ASCIIToUTF16(switches::kNoStartupWindow); 193 } 194 if (in_foreground) { 195 cmd_line += ASCIIToUTF16(" --"); 196 cmd_line += ASCIIToUTF16(switches::kAutoLaunchAtStartup); 197 198 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 199 if (command_line.HasSwitch(switches::kUserDataDir)) { 200 cmd_line += ASCIIToUTF16(" --"); 201 cmd_line += ASCIIToUTF16(switches::kUserDataDir); 202 cmd_line += ASCIIToUTF16("=\""); 203 cmd_line += 204 command_line.GetSwitchValuePath(switches::kUserDataDir).value(); 205 cmd_line += ASCIIToUTF16("\""); 206 } 207 208 cmd_line += ASCIIToUTF16(" --"); 209 cmd_line += ASCIIToUTF16(switches::kProfileDirectory); 210 cmd_line += ASCIIToUTF16("=\""); 211 cmd_line += profile_directory; 212 cmd_line += ASCIIToUTF16("\""); 213 } 214 215 base::win::AddCommandToAutoRun( 216 HKEY_CURRENT_USER, key_name, cmd_line); 217 } else { 218 base::win::RemoveCommandFromAutoRun(HKEY_CURRENT_USER, key_name); 219 } 220} 221 222void DisableAllAutoStartFeatures(const base::string16& profile_directory) { 223 DisableForegroundStartAtLogin(profile_directory); 224 DisableBackgroundStartAtLogin(); 225} 226 227void EnableForegroundStartAtLogin(const base::string16& profile_directory, 228 const base::FilePath& application_path) { 229 SetWillLaunchAtLogin( 230 application_path, profile_directory, FLAG_ENABLE, FLAG_PRESERVE); 231} 232 233void DisableForegroundStartAtLogin(const base::string16& profile_directory) { 234 SetWillLaunchAtLogin( 235 base::FilePath(), profile_directory, FLAG_DISABLE, FLAG_PRESERVE); 236} 237 238void EnableBackgroundStartAtLogin() { 239 // Background mode isn't profile specific, but we specify the Default profile 240 // just to have a unique Run key to attach it to. FilePath is blank because 241 // this function is not called from the installer (see comments for 242 // EnableAutoStartAtLogin). 243 SetWillLaunchAtLogin(base::FilePath(), 244 ASCIIToUTF16(chrome::kInitialProfile), 245 FLAG_PRESERVE, 246 FLAG_ENABLE); 247} 248 249void DisableBackgroundStartAtLogin() { 250 SetWillLaunchAtLogin(base::FilePath(), 251 ASCIIToUTF16(chrome::kInitialProfile), 252 FLAG_PRESERVE, 253 FLAG_DISABLE); 254} 255 256} // namespace auto_launch_util 257