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 // This file defines functions that integrate Chrome in Windows shell. These 6 // functions can be used by Chrome as well as Chrome installer. All of the 7 // work is done by the local functions defined in anonymous namespace in 8 // this class. 9 10 #include "chrome/installer/util/shell_util.h" 11 12 #include <windows.h> 13 #include <shlobj.h> 14 15 #include <limits> 16 #include <string> 17 18 #include "base/bind.h" 19 #include "base/command_line.h" 20 #include "base/files/file_enumerator.h" 21 #include "base/files/file_path.h" 22 #include "base/files/file_util.h" 23 #include "base/lazy_instance.h" 24 #include "base/logging.h" 25 #include "base/md5.h" 26 #include "base/memory/scoped_ptr.h" 27 #include "base/memory/scoped_vector.h" 28 #include "base/path_service.h" 29 #include "base/strings/string16.h" 30 #include "base/strings/string_number_conversions.h" 31 #include "base/strings/string_split.h" 32 #include "base/strings/string_util.h" 33 #include "base/strings/stringprintf.h" 34 #include "base/strings/utf_string_conversions.h" 35 #include "base/synchronization/cancellation_flag.h" 36 #include "base/values.h" 37 #include "base/win/registry.h" 38 #include "base/win/scoped_co_mem.h" 39 #include "base/win/scoped_comptr.h" 40 #include "base/win/shortcut.h" 41 #include "base/win/win_util.h" 42 #include "base/win/windows_version.h" 43 #include "chrome/common/chrome_constants.h" 44 #include "chrome/common/chrome_switches.h" 45 #include "chrome/installer/util/browser_distribution.h" 46 #include "chrome/installer/util/install_util.h" 47 #include "chrome/installer/util/l10n_string_util.h" 48 #include "chrome/installer/util/master_preferences.h" 49 #include "chrome/installer/util/master_preferences_constants.h" 50 #include "chrome/installer/util/util_constants.h" 51 #include "chrome/installer/util/work_item.h" 52 53 #include "installer_util_strings.h" // NOLINT 54 55 using base::win::RegKey; 56 57 namespace { 58 59 // An enum used to tell QuickIsChromeRegistered() which level of registration 60 // the caller wants to confirm. 61 enum RegistrationConfirmationLevel { 62 // Only look for Chrome's ProgIds. 63 // This is sufficient when we are trying to determine the suffix of the 64 // currently running Chrome as shell integration registrations might not be 65 // present. 66 CONFIRM_PROGID_REGISTRATION = 0, 67 // Confirm that Chrome is fully integrated with Windows (i.e. registered with 68 // Defaut Programs). These registrations can be in HKCU as of Windows 8. 69 // Note: Shell registration implies ProgId registration. 70 CONFIRM_SHELL_REGISTRATION, 71 // Same as CONFIRM_SHELL_REGISTRATION, but only look in HKLM (used when 72 // uninstalling to know whether elevation is required to clean up the 73 // registry). 74 CONFIRM_SHELL_REGISTRATION_IN_HKLM, 75 }; 76 77 const wchar_t kReinstallCommand[] = L"ReinstallCommand"; 78 79 // Returns true if Chrome Metro is supported on this OS (Win 8 8370 or greater). 80 // TODO(gab): Change this to a simple check for Win 8 once old Win8 builds 81 // become irrelevant. 82 bool IsChromeMetroSupported() { 83 OSVERSIONINFOEX min_version_info = {}; 84 min_version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); 85 min_version_info.dwMajorVersion = 6; 86 min_version_info.dwMinorVersion = 2; 87 min_version_info.dwBuildNumber = 8370; 88 min_version_info.wServicePackMajor = 0; 89 min_version_info.wServicePackMinor = 0; 90 91 DWORDLONG condition_mask = 0; 92 VER_SET_CONDITION(condition_mask, VER_MAJORVERSION, VER_GREATER_EQUAL); 93 VER_SET_CONDITION(condition_mask, VER_MINORVERSION, VER_GREATER_EQUAL); 94 VER_SET_CONDITION(condition_mask, VER_BUILDNUMBER, VER_GREATER_EQUAL); 95 VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); 96 VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL); 97 98 DWORD type_mask = VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER | 99 VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR; 100 101 return VerifyVersionInfo(&min_version_info, type_mask, condition_mask) != 0; 102 } 103 104 // Returns the current (or installed) browser's ProgId (e.g. 105 // "ChromeHTML|suffix|"). 106 // |suffix| can be the empty string. 107 base::string16 GetBrowserProgId(const base::string16& suffix) { 108 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 109 base::string16 chrome_html(dist->GetBrowserProgIdPrefix()); 110 chrome_html.append(suffix); 111 112 // ProgIds cannot be longer than 39 characters. 113 // Ref: http://msdn.microsoft.com/en-us/library/aa911706.aspx. 114 // Make all new registrations comply with this requirement (existing 115 // registrations must be preserved). 116 base::string16 new_style_suffix; 117 if (ShellUtil::GetUserSpecificRegistrySuffix(&new_style_suffix) && 118 suffix == new_style_suffix && chrome_html.length() > 39) { 119 NOTREACHED(); 120 chrome_html.erase(39); 121 } 122 return chrome_html; 123 } 124 125 // This class is used to initialize and cache a base 32 encoding of the md5 hash 126 // of this user's sid preceded by a dot. 127 // This is guaranteed to be unique on the machine and 27 characters long 128 // (including the '.'). 129 // This is then meant to be used as a suffix on all registrations that may 130 // conflict with another user-level Chrome install. 131 class UserSpecificRegistrySuffix { 132 public: 133 // All the initialization is done in the constructor to be able to build the 134 // suffix in a thread-safe manner when used in conjunction with a 135 // LazyInstance. 136 UserSpecificRegistrySuffix(); 137 138 // Sets |suffix| to the pre-computed suffix cached in this object. 139 // Returns true unless the initialization originally failed. 140 bool GetSuffix(base::string16* suffix); 141 142 private: 143 base::string16 suffix_; 144 145 DISALLOW_COPY_AND_ASSIGN(UserSpecificRegistrySuffix); 146 }; // class UserSpecificRegistrySuffix 147 148 UserSpecificRegistrySuffix::UserSpecificRegistrySuffix() { 149 base::string16 user_sid; 150 if (!base::win::GetUserSidString(&user_sid)) { 151 NOTREACHED(); 152 return; 153 } 154 COMPILE_ASSERT(sizeof(base::MD5Digest) == 16, size_of_MD5_not_as_expected_); 155 base::MD5Digest md5_digest; 156 std::string user_sid_ascii(base::UTF16ToASCII(user_sid)); 157 base::MD5Sum(user_sid_ascii.c_str(), user_sid_ascii.length(), &md5_digest); 158 const base::string16 base32_md5( 159 ShellUtil::ByteArrayToBase32(md5_digest.a, arraysize(md5_digest.a))); 160 // The value returned by the base32 algorithm above must never change and 161 // must always be 26 characters long (i.e. if someone ever moves this to 162 // base and implements the full base32 algorithm (i.e. with appended '=' 163 // signs in the output), they must provide a flag to allow this method to 164 // still request the output with no appended '=' signs). 165 DCHECK_EQ(base32_md5.length(), 26U); 166 suffix_.reserve(base32_md5.length() + 1); 167 suffix_.assign(1, L'.'); 168 suffix_.append(base32_md5); 169 } 170 171 bool UserSpecificRegistrySuffix::GetSuffix(base::string16* suffix) { 172 if (suffix_.empty()) { 173 NOTREACHED(); 174 return false; 175 } 176 suffix->assign(suffix_); 177 return true; 178 } 179 180 // This class represents a single registry entry. The objective is to 181 // encapsulate all the registry entries required for registering Chrome at one 182 // place. This class can not be instantiated outside the class and the objects 183 // of this class type can be obtained only by calling a static method of this 184 // class. 185 class RegistryEntry { 186 public: 187 // A bit-field enum of places to look for this key in the Windows registry. 188 enum LookForIn { 189 LOOK_IN_HKCU = 1 << 0, 190 LOOK_IN_HKLM = 1 << 1, 191 LOOK_IN_HKCU_THEN_HKLM = LOOK_IN_HKCU | LOOK_IN_HKLM, 192 }; 193 194 // Returns the Windows browser client registration key for Chrome. For 195 // example: "Software\Clients\StartMenuInternet\Chromium[.user]". Strictly 196 // speaking, we should use the name of the executable (e.g., "chrome.exe"), 197 // but that ship has sailed. The cost of switching now is re-prompting users 198 // to make Chrome their default browser, which isn't polite. |suffix| is the 199 // user-specific registration suffix; see GetUserSpecificDefaultBrowserSuffix 200 // in shell_util.h for details. 201 static base::string16 GetBrowserClientKey(BrowserDistribution* dist, 202 const base::string16& suffix) { 203 DCHECK(suffix.empty() || suffix[0] == L'.'); 204 return base::string16(ShellUtil::kRegStartMenuInternet) 205 .append(1, L'\\') 206 .append(dist->GetBaseAppName()) 207 .append(suffix); 208 } 209 210 // Returns the Windows Default Programs capabilities key for Chrome. For 211 // example: 212 // "Software\Clients\StartMenuInternet\Chromium[.user]\Capabilities". 213 static base::string16 GetCapabilitiesKey(BrowserDistribution* dist, 214 const base::string16& suffix) { 215 return GetBrowserClientKey(dist, suffix).append(L"\\Capabilities"); 216 } 217 218 // This method returns a list of all the registry entries that 219 // are needed to register this installation's ProgId and AppId. 220 // These entries need to be registered in HKLM prior to Win8. 221 static void GetProgIdEntries(BrowserDistribution* dist, 222 const base::string16& chrome_exe, 223 const base::string16& suffix, 224 ScopedVector<RegistryEntry>* entries) { 225 base::string16 icon_path( 226 ShellUtil::FormatIconLocation( 227 chrome_exe, 228 dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME))); 229 base::string16 open_cmd(ShellUtil::GetChromeShellOpenCmd(chrome_exe)); 230 base::string16 delegate_command( 231 ShellUtil::GetChromeDelegateCommand(chrome_exe)); 232 // For user-level installs: entries for the app id and DelegateExecute verb 233 // handler will be in HKCU; thus we do not need a suffix on those entries. 234 base::string16 app_id( 235 ShellUtil::GetBrowserModelId( 236 dist, InstallUtil::IsPerUserInstall(chrome_exe.c_str()))); 237 base::string16 delegate_guid; 238 bool set_delegate_execute = 239 IsChromeMetroSupported() && 240 dist->GetCommandExecuteImplClsid(&delegate_guid); 241 242 // DelegateExecute ProgId. Needed for Chrome Metro in Windows 8. 243 if (set_delegate_execute) { 244 base::string16 model_id_shell(ShellUtil::kRegClasses); 245 model_id_shell.push_back(base::FilePath::kSeparators[0]); 246 model_id_shell.append(app_id); 247 model_id_shell.append(ShellUtil::kRegExePath); 248 model_id_shell.append(ShellUtil::kRegShellPath); 249 250 // <root hkey>\Software\Classes\<app_id>\.exe\shell @=open 251 entries->push_back(new RegistryEntry(model_id_shell, 252 ShellUtil::kRegVerbOpen)); 253 254 // Each of Chrome's shortcuts has an appid; which, as of Windows 8, is 255 // registered to handle some verbs. This registration has the side-effect 256 // that these verbs now show up in the shortcut's context menu. We 257 // mitigate this side-effect by making the context menu entries 258 // user readable/localized strings. See relevant MSDN article: 259 // http://msdn.microsoft.com/en-US/library/windows/desktop/cc144171.aspx 260 const struct { 261 const wchar_t* verb; 262 int name_id; 263 } verbs[] = { 264 { ShellUtil::kRegVerbOpen, -1 }, 265 { ShellUtil::kRegVerbOpenNewWindow, IDS_SHORTCUT_NEW_WINDOW_BASE }, 266 }; 267 for (size_t i = 0; i < arraysize(verbs); ++i) { 268 base::string16 sub_path(model_id_shell); 269 sub_path.push_back(base::FilePath::kSeparators[0]); 270 sub_path.append(verbs[i].verb); 271 272 // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb> 273 if (verbs[i].name_id != -1) { 274 // TODO(grt): http://crbug.com/75152 Write a reference to a localized 275 // resource. 276 base::string16 verb_name( 277 installer::GetLocalizedString(verbs[i].name_id)); 278 entries->push_back(new RegistryEntry(sub_path, verb_name.c_str())); 279 } 280 entries->push_back(new RegistryEntry( 281 sub_path, L"CommandId", L"Browser.Launch")); 282 283 sub_path.push_back(base::FilePath::kSeparators[0]); 284 sub_path.append(ShellUtil::kRegCommand); 285 286 // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb>\command 287 entries->push_back(new RegistryEntry(sub_path, delegate_command)); 288 entries->push_back(new RegistryEntry( 289 sub_path, ShellUtil::kRegDelegateExecute, delegate_guid)); 290 } 291 } 292 293 // File association ProgId 294 base::string16 chrome_html_prog_id(ShellUtil::kRegClasses); 295 chrome_html_prog_id.push_back(base::FilePath::kSeparators[0]); 296 chrome_html_prog_id.append(GetBrowserProgId(suffix)); 297 entries->push_back(new RegistryEntry( 298 chrome_html_prog_id, dist->GetBrowserProgIdDesc())); 299 entries->push_back(new RegistryEntry( 300 chrome_html_prog_id + ShellUtil::kRegDefaultIcon, icon_path)); 301 entries->push_back(new RegistryEntry( 302 chrome_html_prog_id + ShellUtil::kRegShellOpen, open_cmd)); 303 if (set_delegate_execute) { 304 entries->push_back(new RegistryEntry( 305 chrome_html_prog_id + ShellUtil::kRegShellOpen, 306 ShellUtil::kRegDelegateExecute, delegate_guid)); 307 } 308 309 // The following entries are required as of Windows 8, but do not 310 // depend on the DelegateExecute verb handler being set. 311 if (base::win::GetVersion() >= base::win::VERSION_WIN8) { 312 entries->push_back(new RegistryEntry( 313 chrome_html_prog_id, ShellUtil::kRegAppUserModelId, app_id)); 314 315 // Add \Software\Classes\ChromeHTML\Application entries 316 base::string16 chrome_application(chrome_html_prog_id + 317 ShellUtil::kRegApplication); 318 entries->push_back(new RegistryEntry( 319 chrome_application, ShellUtil::kRegAppUserModelId, app_id)); 320 entries->push_back(new RegistryEntry( 321 chrome_application, ShellUtil::kRegApplicationIcon, icon_path)); 322 // TODO(grt): http://crbug.com/75152 Write a reference to a localized 323 // resource for name, description, and company. 324 entries->push_back(new RegistryEntry( 325 chrome_application, ShellUtil::kRegApplicationName, 326 dist->GetDisplayName())); 327 entries->push_back(new RegistryEntry( 328 chrome_application, ShellUtil::kRegApplicationDescription, 329 dist->GetAppDescription())); 330 entries->push_back(new RegistryEntry( 331 chrome_application, ShellUtil::kRegApplicationCompany, 332 dist->GetPublisherName())); 333 } 334 } 335 336 // This method returns a list of the registry entries needed to declare a 337 // capability of handling a protocol on Windows. 338 static void GetProtocolCapabilityEntries( 339 BrowserDistribution* dist, 340 const base::string16& suffix, 341 const base::string16& protocol, 342 ScopedVector<RegistryEntry>* entries) { 343 entries->push_back(new RegistryEntry( 344 GetCapabilitiesKey(dist, suffix).append(L"\\URLAssociations"), 345 protocol, GetBrowserProgId(suffix))); 346 } 347 348 // This method returns a list of the registry entries required to register 349 // this installation in "RegisteredApplications" on Windows (to appear in 350 // Default Programs, StartMenuInternet, etc.). 351 // These entries need to be registered in HKLM prior to Win8. 352 // If |suffix| is not empty, these entries are guaranteed to be unique on this 353 // machine. 354 static void GetShellIntegrationEntries(BrowserDistribution* dist, 355 const base::string16& chrome_exe, 356 const base::string16& suffix, 357 ScopedVector<RegistryEntry>* entries) { 358 const base::string16 icon_path( 359 ShellUtil::FormatIconLocation( 360 chrome_exe, 361 dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME))); 362 const base::string16 quoted_exe_path(L"\"" + chrome_exe + L"\""); 363 364 // Register for the Start Menu "Internet" link (pre-Win7). 365 const base::string16 start_menu_entry(GetBrowserClientKey(dist, suffix)); 366 // Register Chrome's display name. 367 // TODO(grt): http://crbug.com/75152 Also set LocalizedString; see 368 // http://msdn.microsoft.com/en-us/library/windows/desktop/cc144109(v=VS.85).aspx#registering_the_display_name 369 entries->push_back(new RegistryEntry( 370 start_menu_entry, dist->GetDisplayName())); 371 // Register the "open" verb for launching Chrome via the "Internet" link. 372 entries->push_back(new RegistryEntry( 373 start_menu_entry + ShellUtil::kRegShellOpen, quoted_exe_path)); 374 // Register Chrome's icon for the Start Menu "Internet" link. 375 entries->push_back(new RegistryEntry( 376 start_menu_entry + ShellUtil::kRegDefaultIcon, icon_path)); 377 378 // Register installation information. 379 base::string16 install_info(start_menu_entry + L"\\InstallInfo"); 380 // Note: not using CommandLine since it has ambiguous rules for quoting 381 // strings. 382 entries->push_back(new RegistryEntry(install_info, kReinstallCommand, 383 quoted_exe_path + L" --" + 384 base::ASCIIToWide(switches::kMakeDefaultBrowser))); 385 entries->push_back(new RegistryEntry(install_info, L"HideIconsCommand", 386 quoted_exe_path + L" --" + 387 base::ASCIIToWide(switches::kHideIcons))); 388 entries->push_back(new RegistryEntry(install_info, L"ShowIconsCommand", 389 quoted_exe_path + L" --" + 390 base::ASCIIToWide(switches::kShowIcons))); 391 entries->push_back(new RegistryEntry(install_info, L"IconsVisible", 1)); 392 393 // Register with Default Programs. 394 const base::string16 reg_app_name(dist->GetBaseAppName().append(suffix)); 395 // Tell Windows where to find Chrome's Default Programs info. 396 const base::string16 capabilities(GetCapabilitiesKey(dist, suffix)); 397 entries->push_back(new RegistryEntry(ShellUtil::kRegRegisteredApplications, 398 reg_app_name, capabilities)); 399 // Write out Chrome's Default Programs info. 400 // TODO(grt): http://crbug.com/75152 Write a reference to a localized 401 // resource rather than this. 402 entries->push_back(new RegistryEntry( 403 capabilities, ShellUtil::kRegApplicationDescription, 404 dist->GetLongAppDescription())); 405 entries->push_back(new RegistryEntry( 406 capabilities, ShellUtil::kRegApplicationIcon, icon_path)); 407 entries->push_back(new RegistryEntry( 408 capabilities, ShellUtil::kRegApplicationName, 409 dist->GetDisplayName())); 410 411 entries->push_back(new RegistryEntry(capabilities + L"\\Startmenu", 412 L"StartMenuInternet", reg_app_name)); 413 414 const base::string16 html_prog_id(GetBrowserProgId(suffix)); 415 for (int i = 0; ShellUtil::kPotentialFileAssociations[i] != NULL; i++) { 416 entries->push_back(new RegistryEntry( 417 capabilities + L"\\FileAssociations", 418 ShellUtil::kPotentialFileAssociations[i], html_prog_id)); 419 } 420 for (int i = 0; ShellUtil::kPotentialProtocolAssociations[i] != NULL; 421 i++) { 422 entries->push_back(new RegistryEntry( 423 capabilities + L"\\URLAssociations", 424 ShellUtil::kPotentialProtocolAssociations[i], html_prog_id)); 425 } 426 } 427 428 // This method returns a list of the registry entries required for this 429 // installation to be registered in the Windows shell. 430 // In particular: 431 // - App Paths 432 // http://msdn.microsoft.com/en-us/library/windows/desktop/ee872121 433 // - File Associations 434 // http://msdn.microsoft.com/en-us/library/bb166549 435 // These entries need to be registered in HKLM prior to Win8. 436 static void GetAppRegistrationEntries(const base::string16& chrome_exe, 437 const base::string16& suffix, 438 ScopedVector<RegistryEntry>* entries) { 439 const base::FilePath chrome_path(chrome_exe); 440 base::string16 app_path_key(ShellUtil::kAppPathsRegistryKey); 441 app_path_key.push_back(base::FilePath::kSeparators[0]); 442 app_path_key.append(chrome_path.BaseName().value()); 443 entries->push_back(new RegistryEntry(app_path_key, chrome_exe)); 444 entries->push_back(new RegistryEntry(app_path_key, 445 ShellUtil::kAppPathsRegistryPathName, chrome_path.DirName().value())); 446 447 const base::string16 html_prog_id(GetBrowserProgId(suffix)); 448 for (int i = 0; ShellUtil::kPotentialFileAssociations[i] != NULL; i++) { 449 base::string16 key(ShellUtil::kRegClasses); 450 key.push_back(base::FilePath::kSeparators[0]); 451 key.append(ShellUtil::kPotentialFileAssociations[i]); 452 key.push_back(base::FilePath::kSeparators[0]); 453 key.append(ShellUtil::kRegOpenWithProgids); 454 entries->push_back( 455 new RegistryEntry(key, html_prog_id, base::string16())); 456 } 457 } 458 459 // This method returns a list of all the user level registry entries that 460 // are needed to make Chromium the default handler for a protocol on XP. 461 static void GetXPStyleUserProtocolEntries( 462 const base::string16& protocol, 463 const base::string16& chrome_icon, 464 const base::string16& chrome_open, 465 ScopedVector<RegistryEntry>* entries) { 466 // Protocols associations. 467 base::string16 url_key(ShellUtil::kRegClasses); 468 url_key.push_back(base::FilePath::kSeparators[0]); 469 url_key.append(protocol); 470 471 // This registry value tells Windows that this 'class' is a URL scheme 472 // so IE, explorer and other apps will route it to our handler. 473 // <root hkey>\Software\Classes\<protocol>\URL Protocol 474 entries->push_back(new RegistryEntry(url_key, 475 ShellUtil::kRegUrlProtocol, base::string16())); 476 477 // <root hkey>\Software\Classes\<protocol>\DefaultIcon 478 base::string16 icon_key = url_key + ShellUtil::kRegDefaultIcon; 479 entries->push_back(new RegistryEntry(icon_key, chrome_icon)); 480 481 // <root hkey>\Software\Classes\<protocol>\shell\open\command 482 base::string16 shell_key = url_key + ShellUtil::kRegShellOpen; 483 entries->push_back(new RegistryEntry(shell_key, chrome_open)); 484 485 // <root hkey>\Software\Classes\<protocol>\shell\open\ddeexec 486 base::string16 dde_key = url_key + L"\\shell\\open\\ddeexec"; 487 entries->push_back(new RegistryEntry(dde_key, base::string16())); 488 489 // <root hkey>\Software\Classes\<protocol>\shell\@ 490 base::string16 protocol_shell_key = url_key + ShellUtil::kRegShellPath; 491 entries->push_back(new RegistryEntry(protocol_shell_key, L"open")); 492 } 493 494 // This method returns a list of all the user level registry entries that 495 // are needed to make Chromium default browser on XP. 496 // Some of these entries are irrelevant in recent versions of Windows, but 497 // we register them anyways as some legacy apps are hardcoded to lookup those 498 // values. 499 static void GetXPStyleDefaultBrowserUserEntries( 500 BrowserDistribution* dist, 501 const base::string16& chrome_exe, 502 const base::string16& suffix, 503 ScopedVector<RegistryEntry>* entries) { 504 // File extension associations. 505 base::string16 html_prog_id(GetBrowserProgId(suffix)); 506 for (int i = 0; ShellUtil::kDefaultFileAssociations[i] != NULL; i++) { 507 base::string16 ext_key(ShellUtil::kRegClasses); 508 ext_key.push_back(base::FilePath::kSeparators[0]); 509 ext_key.append(ShellUtil::kDefaultFileAssociations[i]); 510 entries->push_back(new RegistryEntry(ext_key, html_prog_id)); 511 } 512 513 // Protocols associations. 514 base::string16 chrome_open = ShellUtil::GetChromeShellOpenCmd(chrome_exe); 515 base::string16 chrome_icon = 516 ShellUtil::FormatIconLocation( 517 chrome_exe, 518 dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME)); 519 for (int i = 0; ShellUtil::kBrowserProtocolAssociations[i] != NULL; i++) { 520 GetXPStyleUserProtocolEntries(ShellUtil::kBrowserProtocolAssociations[i], 521 chrome_icon, chrome_open, entries); 522 } 523 524 // start->Internet shortcut. 525 base::string16 start_menu(ShellUtil::kRegStartMenuInternet); 526 base::string16 app_name = dist->GetBaseAppName() + suffix; 527 entries->push_back(new RegistryEntry(start_menu, app_name)); 528 } 529 530 // Generate work_item tasks required to create current registry entry and 531 // add them to the given work item list. 532 void AddToWorkItemList(HKEY root, WorkItemList *items) const { 533 items->AddCreateRegKeyWorkItem(root, key_path_, WorkItem::kWow64Default); 534 if (is_string_) { 535 items->AddSetRegValueWorkItem( 536 root, key_path_, WorkItem::kWow64Default, name_, value_, true); 537 } else { 538 items->AddSetRegValueWorkItem( 539 root, key_path_, WorkItem::kWow64Default, name_, int_value_, true); 540 } 541 } 542 543 // Checks if the current registry entry exists in HKCU\|key_path_|\|name_| 544 // and value is |value_|. If the key does NOT exist in HKCU, checks for 545 // the correct name and value in HKLM. 546 // |look_for_in| specifies roots (HKCU and/or HKLM) in which to look for the 547 // key, unspecified roots are not looked into (i.e. the the key is assumed not 548 // to exist in them). 549 // |look_for_in| must at least specify one root to look into. 550 // If |look_for_in| is LOOK_IN_HKCU_THEN_HKLM, this method mimics Windows' 551 // behavior when searching in HKCR (HKCU takes precedence over HKLM). For 552 // registrations outside of HKCR on versions of Windows prior to Win8, 553 // Chrome's values go in HKLM. This function will make unnecessary (but 554 // harmless) queries into HKCU in that case. 555 bool ExistsInRegistry(uint32 look_for_in) const { 556 DCHECK(look_for_in); 557 558 RegistryStatus status = DOES_NOT_EXIST; 559 if (look_for_in & LOOK_IN_HKCU) 560 status = StatusInRegistryUnderRoot(HKEY_CURRENT_USER); 561 if (status == DOES_NOT_EXIST && (look_for_in & LOOK_IN_HKLM)) 562 status = StatusInRegistryUnderRoot(HKEY_LOCAL_MACHINE); 563 return status == SAME_VALUE; 564 } 565 566 private: 567 // States this RegistryKey can be in compared to the registry. 568 enum RegistryStatus { 569 // |name_| does not exist in the registry 570 DOES_NOT_EXIST, 571 // |name_| exists, but its value != |value_| 572 DIFFERENT_VALUE, 573 // |name_| exists and its value is |value_| 574 SAME_VALUE, 575 }; 576 577 // Create a object that represent default value of a key 578 RegistryEntry(const base::string16& key_path, const base::string16& value) 579 : key_path_(key_path), name_(), 580 is_string_(true), value_(value), int_value_(0) { 581 } 582 583 // Create a object that represent a key of type REG_SZ 584 RegistryEntry(const base::string16& key_path, const base::string16& name, 585 const base::string16& value) 586 : key_path_(key_path), name_(name), 587 is_string_(true), value_(value), int_value_(0) { 588 } 589 590 // Create a object that represent a key of integer type 591 RegistryEntry(const base::string16& key_path, const base::string16& name, 592 DWORD value) 593 : key_path_(key_path), name_(name), 594 is_string_(false), value_(), int_value_(value) { 595 } 596 597 base::string16 key_path_; // key path for the registry entry 598 base::string16 name_; // name of the registry entry 599 bool is_string_; // true if current registry entry is of type REG_SZ 600 base::string16 value_; // string value (useful if is_string_ = true) 601 DWORD int_value_; // integer value (useful if is_string_ = false) 602 603 // Helper function for ExistsInRegistry(). 604 // Returns the RegistryStatus of the current registry entry in 605 // |root|\|key_path_|\|name_|. 606 RegistryStatus StatusInRegistryUnderRoot(HKEY root) const { 607 RegKey key(root, key_path_.c_str(), KEY_QUERY_VALUE); 608 bool found = false; 609 bool correct_value = false; 610 if (is_string_) { 611 base::string16 read_value; 612 found = key.ReadValue(name_.c_str(), &read_value) == ERROR_SUCCESS; 613 if (found) { 614 correct_value = read_value.size() == value_.size() && 615 std::equal(value_.begin(), value_.end(), read_value.begin(), 616 base::CaseInsensitiveCompare<wchar_t>()); 617 } 618 } else { 619 DWORD read_value; 620 found = key.ReadValueDW(name_.c_str(), &read_value) == ERROR_SUCCESS; 621 if (found) 622 correct_value = read_value == int_value_; 623 } 624 return found ? 625 (correct_value ? SAME_VALUE : DIFFERENT_VALUE) : DOES_NOT_EXIST; 626 } 627 628 DISALLOW_COPY_AND_ASSIGN(RegistryEntry); 629 }; // class RegistryEntry 630 631 632 // This method converts all the RegistryEntries from the given list to 633 // Set/CreateRegWorkItems and runs them using WorkItemList. 634 bool AddRegistryEntries(HKEY root, const ScopedVector<RegistryEntry>& entries) { 635 scoped_ptr<WorkItemList> items(WorkItem::CreateWorkItemList()); 636 637 for (ScopedVector<RegistryEntry>::const_iterator itr = entries.begin(); 638 itr != entries.end(); ++itr) 639 (*itr)->AddToWorkItemList(root, items.get()); 640 641 // Apply all the registry changes and if there is a problem, rollback 642 if (!items->Do()) { 643 items->Rollback(); 644 return false; 645 } 646 return true; 647 } 648 649 // Checks that all |entries| are present on this computer. 650 // |look_for_in| is passed to RegistryEntry::ExistsInRegistry(). Documentation 651 // for it can be found there. 652 bool AreEntriesRegistered(const ScopedVector<RegistryEntry>& entries, 653 uint32 look_for_in) { 654 bool registered = true; 655 for (ScopedVector<RegistryEntry>::const_iterator itr = entries.begin(); 656 registered && itr != entries.end(); ++itr) { 657 // We do not need registered = registered && ... since the loop condition 658 // is set to exit early. 659 registered = (*itr)->ExistsInRegistry(look_for_in); 660 } 661 return registered; 662 } 663 664 // Checks that all required registry entries for Chrome are already present 665 // on this computer. See RegistryEntry::ExistsInRegistry for the behavior of 666 // |look_for_in|. 667 // Note: between r133333 and r154145 we were registering parts of Chrome in HKCU 668 // and parts in HKLM for user-level installs; we now always register everything 669 // under a single registry root. Not doing so caused http://crbug.com/144910 for 670 // users who first-installed Chrome in that revision range (those users are 671 // still impacted by http://crbug.com/144910). This method will keep returning 672 // true for affected users (i.e. who have all the registrations, but over both 673 // registry roots). 674 bool IsChromeRegistered(BrowserDistribution* dist, 675 const base::string16& chrome_exe, 676 const base::string16& suffix, 677 uint32 look_for_in) { 678 ScopedVector<RegistryEntry> entries; 679 RegistryEntry::GetProgIdEntries(dist, chrome_exe, suffix, &entries); 680 RegistryEntry::GetShellIntegrationEntries(dist, chrome_exe, suffix, &entries); 681 RegistryEntry::GetAppRegistrationEntries(chrome_exe, suffix, &entries); 682 return AreEntriesRegistered(entries, look_for_in); 683 } 684 685 // This method checks if Chrome is already registered on the local machine 686 // for the requested protocol. It just checks the one value required for this. 687 // See RegistryEntry::ExistsInRegistry for the behavior of |look_for_in|. 688 bool IsChromeRegisteredForProtocol(BrowserDistribution* dist, 689 const base::string16& suffix, 690 const base::string16& protocol, 691 uint32 look_for_in) { 692 ScopedVector<RegistryEntry> entries; 693 RegistryEntry::GetProtocolCapabilityEntries(dist, suffix, protocol, &entries); 694 return AreEntriesRegistered(entries, look_for_in); 695 } 696 697 // This method registers Chrome on Vista by launching an elevated setup.exe. 698 // That will show the user the standard Vista elevation prompt. If the user 699 // accepts it the new process will make the necessary changes and return SUCCESS 700 // that we capture and return. 701 // If protocol is non-empty we will also register Chrome as being capable of 702 // handling the protocol. 703 bool ElevateAndRegisterChrome(BrowserDistribution* dist, 704 const base::string16& chrome_exe, 705 const base::string16& suffix, 706 const base::string16& protocol) { 707 // Only user-level installs prior to Windows 8 should need to elevate to 708 // register. 709 DCHECK(InstallUtil::IsPerUserInstall(chrome_exe.c_str())); 710 DCHECK_LT(base::win::GetVersion(), base::win::VERSION_WIN8); 711 base::FilePath exe_path = 712 base::FilePath(chrome_exe).DirName().Append(installer::kSetupExe); 713 if (!base::PathExists(exe_path)) { 714 HKEY reg_root = InstallUtil::IsPerUserInstall(chrome_exe.c_str()) ? 715 HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; 716 RegKey key(reg_root, 717 dist->GetUninstallRegPath().c_str(), 718 KEY_READ | KEY_WOW64_32KEY); 719 base::string16 uninstall_string; 720 key.ReadValue(installer::kUninstallStringField, &uninstall_string); 721 CommandLine command_line = CommandLine::FromString(uninstall_string); 722 exe_path = command_line.GetProgram(); 723 } 724 725 if (base::PathExists(exe_path)) { 726 CommandLine cmd(exe_path); 727 cmd.AppendSwitchNative(installer::switches::kRegisterChromeBrowser, 728 chrome_exe); 729 if (!suffix.empty()) { 730 cmd.AppendSwitchNative( 731 installer::switches::kRegisterChromeBrowserSuffix, suffix); 732 } 733 734 if (!protocol.empty()) { 735 cmd.AppendSwitchNative( 736 installer::switches::kRegisterURLProtocol, protocol); 737 } 738 739 DWORD ret_val = 0; 740 InstallUtil::ExecuteExeAsAdmin(cmd, &ret_val); 741 if (ret_val == 0) 742 return true; 743 } 744 return false; 745 } 746 747 // Launches the Windows 7 and Windows 8 dialog for picking the application to 748 // handle the given protocol. Most importantly, this is used to set the default 749 // handler for http (and, implicitly with it, https). In that case it is also 750 // known as the 'how do you want to open webpages' dialog. 751 // It is required that Chrome be already *registered* for the given protocol. 752 bool LaunchSelectDefaultProtocolHandlerDialog(const wchar_t* protocol) { 753 DCHECK(protocol); 754 OPENASINFO open_as_info = {}; 755 open_as_info.pcszFile = protocol; 756 open_as_info.oaifInFlags = 757 OAIF_URL_PROTOCOL | OAIF_FORCE_REGISTRATION | OAIF_REGISTER_EXT; 758 HRESULT hr = SHOpenWithDialog(NULL, &open_as_info); 759 DLOG_IF(WARNING, FAILED(hr)) << "Failed to set as default " << protocol 760 << " handler; hr=0x" << std::hex << hr; 761 if (FAILED(hr)) 762 return false; 763 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL); 764 return true; 765 } 766 767 // Launches the Windows 7 and Windows 8 application association dialog, which 768 // is the only documented way to make a browser the default browser on 769 // Windows 8. 770 bool LaunchApplicationAssociationDialog(const base::string16& app_id) { 771 base::win::ScopedComPtr<IApplicationAssociationRegistrationUI> aarui; 772 HRESULT hr = aarui.CreateInstance(CLSID_ApplicationAssociationRegistrationUI); 773 if (FAILED(hr)) 774 return false; 775 hr = aarui->LaunchAdvancedAssociationUI(app_id.c_str()); 776 return SUCCEEDED(hr); 777 } 778 779 // Returns true if the current install's |chrome_exe| has been registered with 780 // |suffix|. 781 // |confirmation_level| is the level of verification desired as described in 782 // the RegistrationConfirmationLevel enum above. 783 // |suffix| can be the empty string (this is used to support old installs 784 // where we used to not suffix user-level installs if they were the first to 785 // request the non-suffixed registry entries on the machine). 786 // NOTE: This a quick check that only validates that a single registry entry 787 // points to |chrome_exe|. This should only be used at run-time to determine 788 // how Chrome is registered, not to know whether the registration is complete 789 // at install-time (IsChromeRegistered() can be used for that). 790 bool QuickIsChromeRegistered(BrowserDistribution* dist, 791 const base::string16& chrome_exe, 792 const base::string16& suffix, 793 RegistrationConfirmationLevel confirmation_level) { 794 // Get the appropriate key to look for based on the level desired. 795 base::string16 reg_key; 796 switch (confirmation_level) { 797 case CONFIRM_PROGID_REGISTRATION: 798 // Software\Classes\ChromeHTML|suffix| 799 reg_key = ShellUtil::kRegClasses; 800 reg_key.push_back(base::FilePath::kSeparators[0]); 801 reg_key.append(dist->GetBrowserProgIdPrefix()); 802 reg_key.append(suffix); 803 break; 804 case CONFIRM_SHELL_REGISTRATION: 805 case CONFIRM_SHELL_REGISTRATION_IN_HKLM: 806 // Software\Clients\StartMenuInternet\Google Chrome|suffix| 807 reg_key = RegistryEntry::GetBrowserClientKey(dist, suffix); 808 break; 809 default: 810 NOTREACHED(); 811 break; 812 } 813 reg_key.append(ShellUtil::kRegShellOpen); 814 815 // ProgId registrations are allowed to reside in HKCU for user-level installs 816 // (and values there have priority over values in HKLM). The same is true for 817 // shell integration entries as of Windows 8. 818 if (confirmation_level == CONFIRM_PROGID_REGISTRATION || 819 (confirmation_level == CONFIRM_SHELL_REGISTRATION && 820 base::win::GetVersion() >= base::win::VERSION_WIN8)) { 821 const RegKey key_hkcu(HKEY_CURRENT_USER, reg_key.c_str(), KEY_QUERY_VALUE); 822 base::string16 hkcu_value; 823 // If |reg_key| is present in HKCU, assert that it points to |chrome_exe|. 824 // Otherwise, fall back on an HKLM lookup below. 825 if (key_hkcu.ReadValue(L"", &hkcu_value) == ERROR_SUCCESS) { 826 return InstallUtil::ProgramCompare( 827 base::FilePath(chrome_exe)).Evaluate(hkcu_value); 828 } 829 } 830 831 // Assert that |reg_key| points to |chrome_exe| in HKLM. 832 const RegKey key_hklm(HKEY_LOCAL_MACHINE, reg_key.c_str(), KEY_QUERY_VALUE); 833 base::string16 hklm_value; 834 if (key_hklm.ReadValue(L"", &hklm_value) == ERROR_SUCCESS) { 835 return InstallUtil::ProgramCompare( 836 base::FilePath(chrome_exe)).Evaluate(hklm_value); 837 } 838 return false; 839 } 840 841 // Sets |suffix| to a 27 character string that is specific to this user on this 842 // machine (on user-level installs only). 843 // To support old-style user-level installs however, |suffix| is cleared if the 844 // user currently owns the non-suffixed HKLM registrations. 845 // |suffix| can also be set to the user's username if the current install is 846 // suffixed as per the old-style registrations. 847 // |suffix| is cleared on system-level installs. 848 // |suffix| should then be appended to all Chrome properties that may conflict 849 // with other Chrome user-level installs. 850 // Returns true unless one of the underlying calls fails. 851 bool GetInstallationSpecificSuffix(BrowserDistribution* dist, 852 const base::string16& chrome_exe, 853 base::string16* suffix) { 854 if (!InstallUtil::IsPerUserInstall(chrome_exe.c_str()) || 855 QuickIsChromeRegistered(dist, chrome_exe, base::string16(), 856 CONFIRM_SHELL_REGISTRATION)) { 857 // No suffix on system-level installs and user-level installs already 858 // registered with no suffix. 859 suffix->clear(); 860 return true; 861 } 862 863 // Get the old suffix for the check below. 864 if (!ShellUtil::GetOldUserSpecificRegistrySuffix(suffix)) { 865 NOTREACHED(); 866 return false; 867 } 868 if (QuickIsChromeRegistered(dist, chrome_exe, *suffix, 869 CONFIRM_SHELL_REGISTRATION)) { 870 // Username suffix for installs that are suffixed as per the old-style. 871 return true; 872 } 873 874 return ShellUtil::GetUserSpecificRegistrySuffix(suffix); 875 } 876 877 // Returns the root registry key (HKLM or HKCU) under which registrations must 878 // be placed for this install. As of Windows 8 everything can go in HKCU for 879 // per-user installs. 880 HKEY DetermineRegistrationRoot(bool is_per_user) { 881 return is_per_user && base::win::GetVersion() >= base::win::VERSION_WIN8 ? 882 HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; 883 } 884 885 // Associates Chrome with supported protocols and file associations. This should 886 // not be required on Vista+ but since some applications still read 887 // Software\Classes\http key directly, we have to do this on Vista+ as well. 888 bool RegisterChromeAsDefaultXPStyle(BrowserDistribution* dist, 889 int shell_change, 890 const base::string16& chrome_exe) { 891 bool ret = true; 892 ScopedVector<RegistryEntry> entries; 893 RegistryEntry::GetXPStyleDefaultBrowserUserEntries( 894 dist, chrome_exe, 895 ShellUtil::GetCurrentInstallationSuffix(dist, chrome_exe), &entries); 896 897 // Change the default browser for current user. 898 if ((shell_change & ShellUtil::CURRENT_USER) && 899 !AddRegistryEntries(HKEY_CURRENT_USER, entries)) { 900 ret = false; 901 LOG(ERROR) << "Could not make Chrome default browser (XP/current user)."; 902 } 903 904 // Chrome as default browser at system level. 905 if ((shell_change & ShellUtil::SYSTEM_LEVEL) && 906 !AddRegistryEntries(HKEY_LOCAL_MACHINE, entries)) { 907 ret = false; 908 LOG(ERROR) << "Could not make Chrome default browser (XP/system level)."; 909 } 910 911 return ret; 912 } 913 914 // Associates Chrome with |protocol| in the registry. This should not be 915 // required on Vista+ but since some applications still read these registry 916 // keys directly, we have to do this on Vista+ as well. 917 // See http://msdn.microsoft.com/library/aa767914.aspx for more details. 918 bool RegisterChromeAsDefaultProtocolClientXPStyle( 919 BrowserDistribution* dist, 920 const base::string16& chrome_exe, 921 const base::string16& protocol) { 922 ScopedVector<RegistryEntry> entries; 923 const base::string16 chrome_open( 924 ShellUtil::GetChromeShellOpenCmd(chrome_exe)); 925 const base::string16 chrome_icon( 926 ShellUtil::FormatIconLocation( 927 chrome_exe, 928 dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME))); 929 RegistryEntry::GetXPStyleUserProtocolEntries(protocol, chrome_icon, 930 chrome_open, &entries); 931 // Change the default protocol handler for current user. 932 if (!AddRegistryEntries(HKEY_CURRENT_USER, entries)) { 933 LOG(ERROR) << "Could not make Chrome default protocol client (XP)."; 934 return false; 935 } 936 937 return true; 938 } 939 940 // Returns |properties.shortcut_name| if the property is set, otherwise it 941 // returns dist->GetShortcutName(BrowserDistribution::SHORTCUT_CHROME). In any 942 // case, it makes sure the return value is suffixed with ".lnk". 943 base::string16 ExtractShortcutNameFromProperties( 944 BrowserDistribution* dist, 945 const ShellUtil::ShortcutProperties& properties) { 946 DCHECK(dist); 947 base::string16 shortcut_name; 948 if (properties.has_shortcut_name()) { 949 shortcut_name = properties.shortcut_name; 950 } else { 951 shortcut_name = 952 dist->GetShortcutName(BrowserDistribution::SHORTCUT_CHROME); 953 } 954 955 if (!EndsWith(shortcut_name, installer::kLnkExt, false)) 956 shortcut_name.append(installer::kLnkExt); 957 958 return shortcut_name; 959 } 960 961 // Converts ShellUtil::ShortcutOperation to the best-matching value in 962 // base::win::ShortcutOperation. 963 base::win::ShortcutOperation TranslateShortcutOperation( 964 ShellUtil::ShortcutOperation operation) { 965 switch (operation) { 966 case ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS: // Falls through. 967 case ShellUtil::SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL: 968 return base::win::SHORTCUT_CREATE_ALWAYS; 969 970 case ShellUtil::SHELL_SHORTCUT_UPDATE_EXISTING: 971 return base::win::SHORTCUT_UPDATE_EXISTING; 972 973 case ShellUtil::SHELL_SHORTCUT_REPLACE_EXISTING: 974 return base::win::SHORTCUT_REPLACE_EXISTING; 975 976 default: 977 NOTREACHED(); 978 return base::win::SHORTCUT_REPLACE_EXISTING; 979 } 980 } 981 982 // Returns a base::win::ShortcutProperties struct containing the properties 983 // to set on the shortcut based on the provided ShellUtil::ShortcutProperties. 984 base::win::ShortcutProperties TranslateShortcutProperties( 985 const ShellUtil::ShortcutProperties& properties) { 986 base::win::ShortcutProperties shortcut_properties; 987 988 if (properties.has_target()) { 989 shortcut_properties.set_target(properties.target); 990 DCHECK(!properties.target.DirName().empty()); 991 shortcut_properties.set_working_dir(properties.target.DirName()); 992 } 993 994 if (properties.has_arguments()) 995 shortcut_properties.set_arguments(properties.arguments); 996 997 if (properties.has_description()) 998 shortcut_properties.set_description(properties.description); 999 1000 if (properties.has_icon()) 1001 shortcut_properties.set_icon(properties.icon, properties.icon_index); 1002 1003 if (properties.has_app_id()) 1004 shortcut_properties.set_app_id(properties.app_id); 1005 1006 if (properties.has_dual_mode()) 1007 shortcut_properties.set_dual_mode(properties.dual_mode); 1008 1009 return shortcut_properties; 1010 } 1011 1012 // Cleans up an old verb (run) we used to register in 1013 // <root>\Software\Classes\Chrome<.suffix>\.exe\shell\run on Windows 8. 1014 void RemoveRunVerbOnWindows8(BrowserDistribution* dist, 1015 const base::string16& chrome_exe) { 1016 if (IsChromeMetroSupported()) { 1017 bool is_per_user_install =InstallUtil::IsPerUserInstall(chrome_exe.c_str()); 1018 HKEY root_key = DetermineRegistrationRoot(is_per_user_install); 1019 // There's no need to rollback, so forgo the usual work item lists and just 1020 // remove the key from the registry. 1021 base::string16 run_verb_key(ShellUtil::kRegClasses); 1022 run_verb_key.push_back(base::FilePath::kSeparators[0]); 1023 run_verb_key.append(ShellUtil::GetBrowserModelId( 1024 dist, is_per_user_install)); 1025 run_verb_key.append(ShellUtil::kRegExePath); 1026 run_verb_key.append(ShellUtil::kRegShellPath); 1027 run_verb_key.push_back(base::FilePath::kSeparators[0]); 1028 run_verb_key.append(ShellUtil::kRegVerbRun); 1029 InstallUtil::DeleteRegistryKey(root_key, run_verb_key, 1030 WorkItem::kWow64Default); 1031 } 1032 } 1033 1034 // Gets the short (8.3) form of |path|, putting the result in |short_path| and 1035 // returning true on success. |short_path| is not modified on failure. 1036 bool ShortNameFromPath(const base::FilePath& path, base::string16* short_path) { 1037 DCHECK(short_path); 1038 base::string16 result(MAX_PATH, L'\0'); 1039 DWORD short_length = GetShortPathName(path.value().c_str(), &result[0], 1040 result.size()); 1041 if (short_length == 0 || short_length > result.size()) { 1042 PLOG(ERROR) << "Error getting short (8.3) path"; 1043 return false; 1044 } 1045 1046 result.resize(short_length); 1047 short_path->swap(result); 1048 return true; 1049 } 1050 1051 // Probe using IApplicationAssociationRegistration::QueryCurrentDefault 1052 // (Windows 8); see ProbeProtocolHandlers. This mechanism is not suitable for 1053 // use on previous versions of Windows despite the presence of 1054 // QueryCurrentDefault on them since versions of Windows prior to Windows 8 1055 // did not perform validation on the ProgID registered as the current default. 1056 // As a result, stale ProgIDs could be returned, leading to false positives. 1057 ShellUtil::DefaultState ProbeCurrentDefaultHandlers( 1058 const base::FilePath& chrome_exe, 1059 const wchar_t* const* protocols, 1060 size_t num_protocols) { 1061 base::win::ScopedComPtr<IApplicationAssociationRegistration> registration; 1062 HRESULT hr = registration.CreateInstance( 1063 CLSID_ApplicationAssociationRegistration, NULL, CLSCTX_INPROC); 1064 if (FAILED(hr)) 1065 return ShellUtil::UNKNOWN_DEFAULT; 1066 1067 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 1068 base::string16 prog_id(dist->GetBrowserProgIdPrefix()); 1069 prog_id += ShellUtil::GetCurrentInstallationSuffix(dist, chrome_exe.value()); 1070 1071 for (size_t i = 0; i < num_protocols; ++i) { 1072 base::win::ScopedCoMem<wchar_t> current_app; 1073 hr = registration->QueryCurrentDefault(protocols[i], AT_URLPROTOCOL, 1074 AL_EFFECTIVE, ¤t_app); 1075 if (FAILED(hr) || prog_id.compare(current_app) != 0) 1076 return ShellUtil::NOT_DEFAULT; 1077 } 1078 1079 return ShellUtil::IS_DEFAULT; 1080 } 1081 1082 // Probe using IApplicationAssociationRegistration::QueryAppIsDefault (Vista and 1083 // Windows 7); see ProbeProtocolHandlers. 1084 ShellUtil::DefaultState ProbeAppIsDefaultHandlers( 1085 const base::FilePath& chrome_exe, 1086 const wchar_t* const* protocols, 1087 size_t num_protocols) { 1088 base::win::ScopedComPtr<IApplicationAssociationRegistration> registration; 1089 HRESULT hr = registration.CreateInstance( 1090 CLSID_ApplicationAssociationRegistration, NULL, CLSCTX_INPROC); 1091 if (FAILED(hr)) 1092 return ShellUtil::UNKNOWN_DEFAULT; 1093 1094 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 1095 base::string16 app_name( 1096 ShellUtil::GetApplicationName(dist, chrome_exe.value())); 1097 1098 BOOL result; 1099 for (size_t i = 0; i < num_protocols; ++i) { 1100 result = TRUE; 1101 hr = registration->QueryAppIsDefault(protocols[i], AT_URLPROTOCOL, 1102 AL_EFFECTIVE, app_name.c_str(), &result); 1103 if (FAILED(hr) || result == FALSE) 1104 return ShellUtil::NOT_DEFAULT; 1105 } 1106 1107 return ShellUtil::IS_DEFAULT; 1108 } 1109 1110 // Probe the current commands registered to handle the shell "open" verb for 1111 // |protocols| (Windows XP); see ProbeProtocolHandlers. 1112 ShellUtil::DefaultState ProbeOpenCommandHandlers( 1113 const base::FilePath& chrome_exe, 1114 const wchar_t* const* protocols, 1115 size_t num_protocols) { 1116 // Get its short (8.3) form. 1117 base::string16 short_app_path; 1118 if (!ShortNameFromPath(chrome_exe, &short_app_path)) 1119 return ShellUtil::UNKNOWN_DEFAULT; 1120 1121 const HKEY root_key = HKEY_CLASSES_ROOT; 1122 base::string16 key_path; 1123 base::win::RegKey key; 1124 base::string16 value; 1125 CommandLine command_line(CommandLine::NO_PROGRAM); 1126 base::string16 short_path; 1127 1128 for (size_t i = 0; i < num_protocols; ++i) { 1129 // Get the command line from HKCU\<protocol>\shell\open\command. 1130 key_path.assign(protocols[i]).append(ShellUtil::kRegShellOpen); 1131 if ((key.Open(root_key, key_path.c_str(), 1132 KEY_QUERY_VALUE) != ERROR_SUCCESS) || 1133 (key.ReadValue(L"", &value) != ERROR_SUCCESS)) { 1134 return ShellUtil::NOT_DEFAULT; 1135 } 1136 1137 // Need to normalize path in case it's been munged. 1138 command_line = CommandLine::FromString(value); 1139 if (!ShortNameFromPath(command_line.GetProgram(), &short_path)) 1140 return ShellUtil::UNKNOWN_DEFAULT; 1141 1142 if (!base::FilePath::CompareEqualIgnoreCase(short_path, short_app_path)) 1143 return ShellUtil::NOT_DEFAULT; 1144 } 1145 1146 return ShellUtil::IS_DEFAULT; 1147 } 1148 1149 // A helper function that probes default protocol handler registration (in a 1150 // manner appropriate for the current version of Windows) to determine if 1151 // Chrome is the default handler for |protocols|. Returns IS_DEFAULT 1152 // only if Chrome is the default for all specified protocols. 1153 ShellUtil::DefaultState ProbeProtocolHandlers( 1154 const base::FilePath& chrome_exe, 1155 const wchar_t* const* protocols, 1156 size_t num_protocols) { 1157 #if DCHECK_IS_ON 1158 DCHECK(!num_protocols || protocols); 1159 for (size_t i = 0; i < num_protocols; ++i) 1160 DCHECK(protocols[i] && *protocols[i]); 1161 #endif 1162 1163 const base::win::Version windows_version = base::win::GetVersion(); 1164 1165 if (windows_version >= base::win::VERSION_WIN8) 1166 return ProbeCurrentDefaultHandlers(chrome_exe, protocols, num_protocols); 1167 else if (windows_version >= base::win::VERSION_VISTA) 1168 return ProbeAppIsDefaultHandlers(chrome_exe, protocols, num_protocols); 1169 1170 return ProbeOpenCommandHandlers(chrome_exe, protocols, num_protocols); 1171 } 1172 1173 // (Windows 8+) Finds and stores an app shortcuts folder path in *|path|. 1174 // Returns true on success. 1175 bool GetAppShortcutsFolder(BrowserDistribution* dist, 1176 ShellUtil::ShellChange level, 1177 base::FilePath *path) { 1178 DCHECK(path); 1179 DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8); 1180 1181 base::FilePath folder; 1182 if (!PathService::Get(base::DIR_APP_SHORTCUTS, &folder)) { 1183 LOG(ERROR) << "Could not get application shortcuts location."; 1184 return false; 1185 } 1186 1187 folder = folder.Append( 1188 ShellUtil::GetBrowserModelId(dist, level == ShellUtil::CURRENT_USER)); 1189 if (!base::DirectoryExists(folder)) { 1190 VLOG(1) << "No start screen shortcuts."; 1191 return false; 1192 } 1193 1194 *path = folder; 1195 return true; 1196 } 1197 1198 // Shortcut filters for BatchShortcutAction(). 1199 1200 typedef base::Callback<bool(const base::FilePath& /*shortcut_path*/, 1201 const base::string16& /*args*/)> 1202 ShortcutFilterCallback; 1203 1204 // FilterTargetEq is a shortcut filter that matches only shortcuts that have a 1205 // specific target, and optionally matches shortcuts that have non-empty 1206 // arguments. 1207 class FilterTargetEq { 1208 public: 1209 FilterTargetEq(const base::FilePath& desired_target_exe, bool require_args); 1210 1211 // Returns true if filter rules are satisfied, i.e.: 1212 // - |target_path|'s target == |desired_target_compare_|, and 1213 // - |args| is non-empty (if |require_args_| == true). 1214 bool Match(const base::FilePath& target_path, 1215 const base::string16& args) const; 1216 1217 // A convenience routine to create a callback to call Match(). 1218 // The callback is only valid during the lifetime of the FilterTargetEq 1219 // instance. 1220 ShortcutFilterCallback AsShortcutFilterCallback(); 1221 1222 private: 1223 InstallUtil::ProgramCompare desired_target_compare_; 1224 1225 bool require_args_; 1226 }; 1227 1228 FilterTargetEq::FilterTargetEq(const base::FilePath& desired_target_exe, 1229 bool require_args) 1230 : desired_target_compare_(desired_target_exe), 1231 require_args_(require_args) {} 1232 1233 bool FilterTargetEq::Match(const base::FilePath& target_path, 1234 const base::string16& args) const { 1235 if (!desired_target_compare_.EvaluatePath(target_path)) 1236 return false; 1237 if (require_args_ && args.empty()) 1238 return false; 1239 return true; 1240 } 1241 1242 ShortcutFilterCallback FilterTargetEq::AsShortcutFilterCallback() { 1243 return base::Bind(&FilterTargetEq::Match, base::Unretained(this)); 1244 } 1245 1246 // Shortcut operations for BatchShortcutAction(). 1247 1248 typedef base::Callback<bool(const base::FilePath& /*shortcut_path*/)> 1249 ShortcutOperationCallback; 1250 1251 bool ShortcutOpUnpin(const base::FilePath& shortcut_path) { 1252 VLOG(1) << "Trying to unpin " << shortcut_path.value(); 1253 if (!base::win::TaskbarUnpinShortcutLink(shortcut_path.value().c_str())) { 1254 VLOG(1) << shortcut_path.value() << " wasn't pinned (or the unpin failed)."; 1255 // No error, since shortcut might not be pinned. 1256 } 1257 return true; 1258 } 1259 1260 bool ShortcutOpDelete(const base::FilePath& shortcut_path) { 1261 bool ret = base::DeleteFile(shortcut_path, false); 1262 LOG_IF(ERROR, !ret) << "Failed to remove " << shortcut_path.value(); 1263 return ret; 1264 } 1265 1266 bool ShortcutOpRetarget(const base::FilePath& old_target, 1267 const base::FilePath& new_target, 1268 const base::FilePath& shortcut_path) { 1269 base::win::ShortcutProperties new_prop; 1270 new_prop.set_target(new_target); 1271 1272 // If the old icon matches old target, then update icon while keeping the old 1273 // icon index. Non-fatal if we fail to get the old icon. 1274 base::win::ShortcutProperties old_prop; 1275 if (base::win::ResolveShortcutProperties( 1276 shortcut_path, 1277 base::win::ShortcutProperties::PROPERTIES_ICON, 1278 &old_prop)) { 1279 if (InstallUtil::ProgramCompare(old_target).EvaluatePath(old_prop.icon)) 1280 new_prop.set_icon(new_target, old_prop.icon_index); 1281 } else { 1282 LOG(ERROR) << "Failed to resolve " << shortcut_path.value(); 1283 } 1284 1285 bool result = base::win::CreateOrUpdateShortcutLink( 1286 shortcut_path, new_prop, base::win::SHORTCUT_UPDATE_EXISTING); 1287 LOG_IF(ERROR, !result) << "Failed to retarget " << shortcut_path.value(); 1288 return result; 1289 } 1290 1291 bool ShortcutOpListOrRemoveUnknownArgs( 1292 bool do_removal, 1293 std::vector<std::pair<base::FilePath, base::string16> >* shortcuts, 1294 const base::FilePath& shortcut_path) { 1295 base::string16 args; 1296 if (!base::win::ResolveShortcut(shortcut_path, NULL, &args)) 1297 return false; 1298 1299 CommandLine current_args(CommandLine::FromString(base::StringPrintf( 1300 L"unused_program %ls", args.c_str()))); 1301 const char* const kept_switches[] = { 1302 switches::kApp, 1303 switches::kAppId, 1304 switches::kShowAppList, 1305 switches::kProfileDirectory, 1306 }; 1307 CommandLine desired_args(CommandLine::NO_PROGRAM); 1308 desired_args.CopySwitchesFrom(current_args, kept_switches, 1309 arraysize(kept_switches)); 1310 if (desired_args.argv().size() == current_args.argv().size()) 1311 return true; 1312 if (shortcuts) 1313 shortcuts->push_back(std::make_pair(shortcut_path, args)); 1314 if (!do_removal) 1315 return true; 1316 base::win::ShortcutProperties updated_properties; 1317 updated_properties.set_arguments(desired_args.GetArgumentsString()); 1318 return base::win::CreateOrUpdateShortcutLink( 1319 shortcut_path, updated_properties, base::win::SHORTCUT_UPDATE_EXISTING); 1320 } 1321 1322 // {|location|, |dist|, |level|} determine |shortcut_folder|. 1323 // For each shortcut in |shortcut_folder| that match |shortcut_filter|, apply 1324 // |shortcut_operation|. Returns true if all operations are successful. 1325 // All intended operations are attempted, even if failures occur. 1326 // This method will abort and return false if |cancel| is non-NULL and gets set 1327 // at any point during this call. 1328 bool BatchShortcutAction( 1329 const ShortcutFilterCallback& shortcut_filter, 1330 const ShortcutOperationCallback& shortcut_operation, 1331 ShellUtil::ShortcutLocation location, 1332 BrowserDistribution* dist, 1333 ShellUtil::ShellChange level, 1334 const scoped_refptr<ShellUtil::SharedCancellationFlag>& cancel) { 1335 DCHECK(!shortcut_operation.is_null()); 1336 1337 // There is no system-level Quick Launch shortcut folder. 1338 if (level == ShellUtil::SYSTEM_LEVEL && 1339 location == ShellUtil::SHORTCUT_LOCATION_QUICK_LAUNCH) { 1340 return true; 1341 } 1342 1343 base::FilePath shortcut_folder; 1344 if (!ShellUtil::GetShortcutPath(location, dist, level, &shortcut_folder)) { 1345 LOG(WARNING) << "Cannot find path at location " << location; 1346 return false; 1347 } 1348 1349 bool success = true; 1350 base::FileEnumerator enumerator( 1351 shortcut_folder, false, base::FileEnumerator::FILES, 1352 base::string16(L"*") + installer::kLnkExt); 1353 base::FilePath target_path; 1354 base::string16 args; 1355 for (base::FilePath shortcut_path = enumerator.Next(); 1356 !shortcut_path.empty(); 1357 shortcut_path = enumerator.Next()) { 1358 if (cancel && cancel->data.IsSet()) 1359 return false; 1360 if (base::win::ResolveShortcut(shortcut_path, &target_path, &args)) { 1361 if (shortcut_filter.Run(target_path, args) && 1362 !shortcut_operation.Run(shortcut_path)) { 1363 success = false; 1364 } 1365 } else { 1366 LOG(ERROR) << "Cannot resolve shortcut at " << shortcut_path.value(); 1367 success = false; 1368 } 1369 } 1370 return success; 1371 } 1372 1373 1374 // If the folder specified by {|location|, |dist|, |level|} is empty, remove it. 1375 // Otherwise do nothing. Returns true on success, including the vacuous case 1376 // where no deletion occurred because directory is non-empty. 1377 bool RemoveShortcutFolderIfEmpty(ShellUtil::ShortcutLocation location, 1378 BrowserDistribution* dist, 1379 ShellUtil::ShellChange level) { 1380 // Explicitly whitelist locations, since accidental calls can be very harmful. 1381 if (location != ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR && 1382 location != ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR && 1383 location != ShellUtil::SHORTCUT_LOCATION_APP_SHORTCUTS) { 1384 NOTREACHED(); 1385 return false; 1386 } 1387 1388 base::FilePath shortcut_folder; 1389 if (!ShellUtil::GetShortcutPath(location, dist, level, &shortcut_folder)) { 1390 LOG(WARNING) << "Cannot find path at location " << location; 1391 return false; 1392 } 1393 if (base::IsDirectoryEmpty(shortcut_folder) && 1394 !base::DeleteFile(shortcut_folder, true)) { 1395 LOG(ERROR) << "Cannot remove folder " << shortcut_folder.value(); 1396 return false; 1397 } 1398 return true; 1399 } 1400 1401 } // namespace 1402 1403 const wchar_t* ShellUtil::kRegDefaultIcon = L"\\DefaultIcon"; 1404 const wchar_t* ShellUtil::kRegShellPath = L"\\shell"; 1405 const wchar_t* ShellUtil::kRegShellOpen = L"\\shell\\open\\command"; 1406 const wchar_t* ShellUtil::kRegStartMenuInternet = 1407 L"Software\\Clients\\StartMenuInternet"; 1408 const wchar_t* ShellUtil::kRegClasses = L"Software\\Classes"; 1409 const wchar_t* ShellUtil::kRegRegisteredApplications = 1410 L"Software\\RegisteredApplications"; 1411 const wchar_t* ShellUtil::kRegVistaUrlPrefs = 1412 L"Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\" 1413 L"http\\UserChoice"; 1414 const wchar_t* ShellUtil::kAppPathsRegistryKey = 1415 L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths"; 1416 const wchar_t* ShellUtil::kAppPathsRegistryPathName = L"Path"; 1417 1418 const wchar_t* ShellUtil::kDefaultFileAssociations[] = {L".htm", L".html", 1419 L".shtml", L".xht", L".xhtml", NULL}; 1420 const wchar_t* ShellUtil::kPotentialFileAssociations[] = {L".htm", L".html", 1421 L".shtml", L".xht", L".xhtml", L".webp", NULL}; 1422 const wchar_t* ShellUtil::kBrowserProtocolAssociations[] = {L"ftp", L"http", 1423 L"https", NULL}; 1424 const wchar_t* ShellUtil::kPotentialProtocolAssociations[] = {L"ftp", L"http", 1425 L"https", L"irc", L"mailto", L"mms", L"news", L"nntp", L"sms", L"smsto", 1426 L"tel", L"urn", L"webcal", NULL}; 1427 const wchar_t* ShellUtil::kRegUrlProtocol = L"URL Protocol"; 1428 const wchar_t* ShellUtil::kRegApplication = L"\\Application"; 1429 const wchar_t* ShellUtil::kRegAppUserModelId = L"AppUserModelId"; 1430 const wchar_t* ShellUtil::kRegApplicationDescription = 1431 L"ApplicationDescription"; 1432 const wchar_t* ShellUtil::kRegApplicationName = L"ApplicationName"; 1433 const wchar_t* ShellUtil::kRegApplicationIcon = L"ApplicationIcon"; 1434 const wchar_t* ShellUtil::kRegApplicationCompany = L"ApplicationCompany"; 1435 const wchar_t* ShellUtil::kRegExePath = L"\\.exe"; 1436 const wchar_t* ShellUtil::kRegVerbOpen = L"open"; 1437 const wchar_t* ShellUtil::kRegVerbOpenNewWindow = L"opennewwindow"; 1438 const wchar_t* ShellUtil::kRegVerbRun = L"run"; 1439 const wchar_t* ShellUtil::kRegCommand = L"command"; 1440 const wchar_t* ShellUtil::kRegDelegateExecute = L"DelegateExecute"; 1441 const wchar_t* ShellUtil::kRegOpenWithProgids = L"OpenWithProgids"; 1442 1443 bool ShellUtil::QuickIsChromeRegisteredInHKLM(BrowserDistribution* dist, 1444 const base::string16& chrome_exe, 1445 const base::string16& suffix) { 1446 return QuickIsChromeRegistered(dist, chrome_exe, suffix, 1447 CONFIRM_SHELL_REGISTRATION_IN_HKLM); 1448 } 1449 1450 bool ShellUtil::ShortcutLocationIsSupported( 1451 ShellUtil::ShortcutLocation location) { 1452 switch (location) { 1453 case SHORTCUT_LOCATION_DESKTOP: // Falls through. 1454 case SHORTCUT_LOCATION_QUICK_LAUNCH: // Falls through. 1455 case SHORTCUT_LOCATION_START_MENU_ROOT: // Falls through. 1456 case SHORTCUT_LOCATION_START_MENU_CHROME_DIR: // Falls through. 1457 case SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR: 1458 return true; 1459 case SHORTCUT_LOCATION_TASKBAR_PINS: 1460 return base::win::GetVersion() >= base::win::VERSION_WIN7; 1461 case SHORTCUT_LOCATION_APP_SHORTCUTS: 1462 return base::win::GetVersion() >= base::win::VERSION_WIN8; 1463 default: 1464 NOTREACHED(); 1465 return false; 1466 } 1467 } 1468 1469 bool ShellUtil::GetShortcutPath(ShellUtil::ShortcutLocation location, 1470 BrowserDistribution* dist, 1471 ShellChange level, 1472 base::FilePath* path) { 1473 DCHECK(path); 1474 int dir_key = -1; 1475 base::string16 folder_to_append; 1476 switch (location) { 1477 case SHORTCUT_LOCATION_DESKTOP: 1478 dir_key = (level == CURRENT_USER) ? base::DIR_USER_DESKTOP : 1479 base::DIR_COMMON_DESKTOP; 1480 break; 1481 case SHORTCUT_LOCATION_QUICK_LAUNCH: 1482 // There is no support for a system-level Quick Launch shortcut. 1483 DCHECK_EQ(level, CURRENT_USER); 1484 dir_key = base::DIR_USER_QUICK_LAUNCH; 1485 break; 1486 case SHORTCUT_LOCATION_START_MENU_ROOT: 1487 dir_key = (level == CURRENT_USER) ? base::DIR_START_MENU : 1488 base::DIR_COMMON_START_MENU; 1489 break; 1490 case SHORTCUT_LOCATION_START_MENU_CHROME_DIR: 1491 dir_key = (level == CURRENT_USER) ? base::DIR_START_MENU : 1492 base::DIR_COMMON_START_MENU; 1493 folder_to_append = dist->GetStartMenuShortcutSubfolder( 1494 BrowserDistribution::SUBFOLDER_CHROME); 1495 break; 1496 case SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR: 1497 dir_key = (level == CURRENT_USER) ? base::DIR_START_MENU : 1498 base::DIR_COMMON_START_MENU; 1499 folder_to_append = dist->GetStartMenuShortcutSubfolder( 1500 BrowserDistribution::SUBFOLDER_APPS); 1501 break; 1502 case SHORTCUT_LOCATION_TASKBAR_PINS: 1503 dir_key = base::DIR_TASKBAR_PINS; 1504 break; 1505 case SHORTCUT_LOCATION_APP_SHORTCUTS: 1506 // TODO(huangs): Move GetAppShortcutsFolder() logic into base_paths_win. 1507 return GetAppShortcutsFolder(dist, level, path); 1508 1509 default: 1510 NOTREACHED(); 1511 return false; 1512 } 1513 1514 if (!PathService::Get(dir_key, path) || path->empty()) { 1515 NOTREACHED() << dir_key; 1516 return false; 1517 } 1518 1519 if (!folder_to_append.empty()) 1520 *path = path->Append(folder_to_append); 1521 1522 return true; 1523 } 1524 1525 bool ShellUtil::CreateOrUpdateShortcut( 1526 ShellUtil::ShortcutLocation location, 1527 BrowserDistribution* dist, 1528 const ShellUtil::ShortcutProperties& properties, 1529 ShellUtil::ShortcutOperation operation) { 1530 // Explicitly whitelist locations to which this is applicable. 1531 if (location != SHORTCUT_LOCATION_DESKTOP && 1532 location != SHORTCUT_LOCATION_QUICK_LAUNCH && 1533 location != SHORTCUT_LOCATION_START_MENU_ROOT && 1534 location != SHORTCUT_LOCATION_START_MENU_CHROME_DIR && 1535 location != SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR) { 1536 NOTREACHED(); 1537 return false; 1538 } 1539 1540 DCHECK(dist); 1541 // |pin_to_taskbar| is only acknowledged when first creating the shortcut. 1542 DCHECK(!properties.pin_to_taskbar || 1543 operation == SHELL_SHORTCUT_CREATE_ALWAYS || 1544 operation == SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL); 1545 1546 base::FilePath user_shortcut_path; 1547 base::FilePath system_shortcut_path; 1548 if (location == SHORTCUT_LOCATION_QUICK_LAUNCH) { 1549 // There is no system-level shortcut for Quick Launch. 1550 DCHECK_EQ(properties.level, CURRENT_USER); 1551 } else if (!GetShortcutPath( 1552 location, dist, SYSTEM_LEVEL, &system_shortcut_path)) { 1553 NOTREACHED(); 1554 return false; 1555 } 1556 1557 base::string16 shortcut_name( 1558 ExtractShortcutNameFromProperties(dist, properties)); 1559 system_shortcut_path = system_shortcut_path.Append(shortcut_name); 1560 1561 base::FilePath* chosen_path; 1562 bool should_install_shortcut = true; 1563 if (properties.level == SYSTEM_LEVEL) { 1564 // Install the system-level shortcut if requested. 1565 chosen_path = &system_shortcut_path; 1566 } else if (operation != SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL || 1567 system_shortcut_path.empty() || 1568 !base::PathExists(system_shortcut_path)) { 1569 // Otherwise install the user-level shortcut, unless the system-level 1570 // variant of this shortcut is present on the machine and |operation| states 1571 // not to create a user-level shortcut in that case. 1572 if (!GetShortcutPath(location, dist, CURRENT_USER, &user_shortcut_path)) { 1573 NOTREACHED(); 1574 return false; 1575 } 1576 user_shortcut_path = user_shortcut_path.Append(shortcut_name); 1577 chosen_path = &user_shortcut_path; 1578 } else { 1579 // Do not install any shortcut if we are told to install a user-level 1580 // shortcut, but the system-level variant of that shortcut is present. 1581 // Other actions (e.g., pinning) can still happen with respect to the 1582 // existing system-level shortcut however. 1583 chosen_path = &system_shortcut_path; 1584 should_install_shortcut = false; 1585 } 1586 1587 if (chosen_path == NULL || chosen_path->empty()) { 1588 NOTREACHED(); 1589 return false; 1590 } 1591 1592 base::win::ShortcutOperation shortcut_operation = 1593 TranslateShortcutOperation(operation); 1594 bool ret = true; 1595 if (should_install_shortcut) { 1596 // Make sure the parent directories exist when creating the shortcut. 1597 if (shortcut_operation == base::win::SHORTCUT_CREATE_ALWAYS && 1598 !base::CreateDirectory(chosen_path->DirName())) { 1599 NOTREACHED(); 1600 return false; 1601 } 1602 1603 base::win::ShortcutProperties shortcut_properties( 1604 TranslateShortcutProperties(properties)); 1605 ret = base::win::CreateOrUpdateShortcutLink( 1606 *chosen_path, shortcut_properties, shortcut_operation); 1607 } 1608 1609 if (ret && shortcut_operation == base::win::SHORTCUT_CREATE_ALWAYS && 1610 properties.pin_to_taskbar && 1611 base::win::GetVersion() >= base::win::VERSION_WIN7) { 1612 ret = base::win::TaskbarPinShortcutLink(chosen_path->value().c_str()); 1613 if (!ret) { 1614 LOG(ERROR) << "Failed to pin " << chosen_path->value(); 1615 } 1616 } 1617 1618 return ret; 1619 } 1620 1621 base::string16 ShellUtil::FormatIconLocation(const base::string16& icon_path, 1622 int icon_index) { 1623 base::string16 icon_string(icon_path); 1624 icon_string.append(L","); 1625 icon_string.append(base::IntToString16(icon_index)); 1626 return icon_string; 1627 } 1628 1629 base::string16 ShellUtil::GetChromeShellOpenCmd( 1630 const base::string16& chrome_exe) { 1631 return L"\"" + chrome_exe + L"\" -- \"%1\""; 1632 } 1633 1634 base::string16 ShellUtil::GetChromeDelegateCommand( 1635 const base::string16& chrome_exe) { 1636 return L"\"" + chrome_exe + L"\" -- %*"; 1637 } 1638 1639 void ShellUtil::GetRegisteredBrowsers( 1640 BrowserDistribution* dist, 1641 std::map<base::string16, base::string16>* browsers) { 1642 DCHECK(dist); 1643 DCHECK(browsers); 1644 1645 const base::string16 base_key(ShellUtil::kRegStartMenuInternet); 1646 base::string16 client_path; 1647 RegKey key; 1648 base::string16 name; 1649 base::string16 command; 1650 1651 // HKCU has precedence over HKLM for these registrations: http://goo.gl/xjczJ. 1652 // Look in HKCU second to override any identical values found in HKLM. 1653 const HKEY roots[] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER }; 1654 for (int i = 0; i < arraysize(roots); ++i) { 1655 const HKEY root = roots[i]; 1656 for (base::win::RegistryKeyIterator iter(root, base_key.c_str()); 1657 iter.Valid(); ++iter) { 1658 client_path.assign(base_key).append(1, L'\\').append(iter.Name()); 1659 // Read the browser's name (localized according to install language). 1660 if (key.Open(root, client_path.c_str(), 1661 KEY_QUERY_VALUE) != ERROR_SUCCESS || 1662 key.ReadValue(NULL, &name) != ERROR_SUCCESS || 1663 name.empty() || 1664 name.find(dist->GetBaseAppName()) != base::string16::npos) { 1665 continue; 1666 } 1667 // Read the browser's reinstall command. 1668 if (key.Open(root, (client_path + L"\\InstallInfo").c_str(), 1669 KEY_QUERY_VALUE) == ERROR_SUCCESS && 1670 key.ReadValue(kReinstallCommand, &command) == ERROR_SUCCESS && 1671 !command.empty()) { 1672 (*browsers)[name] = command; 1673 } 1674 } 1675 } 1676 } 1677 1678 base::string16 ShellUtil::GetCurrentInstallationSuffix( 1679 BrowserDistribution* dist, 1680 const base::string16& chrome_exe) { 1681 // This method is somewhat the opposite of GetInstallationSpecificSuffix(). 1682 // In this case we are not trying to determine the current suffix for the 1683 // upcoming installation (i.e. not trying to stick to a currently bad 1684 // registration style if one is present). 1685 // Here we want to determine which suffix we should use at run-time. 1686 // In order of preference, we prefer (for user-level installs): 1687 // 1) Base 32 encoding of the md5 hash of the user's sid (new-style). 1688 // 2) Username (old-style). 1689 // 3) Unsuffixed (even worse). 1690 base::string16 tested_suffix; 1691 if (InstallUtil::IsPerUserInstall(chrome_exe.c_str()) && 1692 (!GetUserSpecificRegistrySuffix(&tested_suffix) || 1693 !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix, 1694 CONFIRM_PROGID_REGISTRATION)) && 1695 (!GetOldUserSpecificRegistrySuffix(&tested_suffix) || 1696 !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix, 1697 CONFIRM_PROGID_REGISTRATION)) && 1698 !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix.erase(), 1699 CONFIRM_PROGID_REGISTRATION)) { 1700 // If Chrome is not registered under any of the possible suffixes (e.g. 1701 // tests, Canary, etc.): use the new-style suffix at run-time. 1702 if (!GetUserSpecificRegistrySuffix(&tested_suffix)) 1703 NOTREACHED(); 1704 } 1705 return tested_suffix; 1706 } 1707 1708 base::string16 ShellUtil::GetApplicationName(BrowserDistribution* dist, 1709 const base::string16& chrome_exe) { 1710 base::string16 app_name = dist->GetBaseAppName(); 1711 app_name += GetCurrentInstallationSuffix(dist, chrome_exe); 1712 return app_name; 1713 } 1714 1715 base::string16 ShellUtil::GetBrowserModelId(BrowserDistribution* dist, 1716 bool is_per_user_install) { 1717 base::string16 app_id(dist->GetBaseAppId()); 1718 base::string16 suffix; 1719 1720 // TODO(robertshield): Temporary hack to make the kRegisterChromeBrowserSuffix 1721 // apply to all registry values computed down in these murky depths. 1722 CommandLine& command_line = *CommandLine::ForCurrentProcess(); 1723 if (command_line.HasSwitch( 1724 installer::switches::kRegisterChromeBrowserSuffix)) { 1725 suffix = command_line.GetSwitchValueNative( 1726 installer::switches::kRegisterChromeBrowserSuffix); 1727 } else if (is_per_user_install && !GetUserSpecificRegistrySuffix(&suffix)) { 1728 NOTREACHED(); 1729 } 1730 // There is only one component (i.e. the suffixed appid) in this case, but it 1731 // is still necessary to go through the appid constructor to make sure the 1732 // returned appid is truncated if necessary. 1733 std::vector<base::string16> components(1, app_id.append(suffix)); 1734 return BuildAppModelId(components); 1735 } 1736 1737 base::string16 ShellUtil::BuildAppModelId( 1738 const std::vector<base::string16>& components) { 1739 DCHECK_GT(components.size(), 0U); 1740 1741 // Find the maximum numbers of characters allowed in each component 1742 // (accounting for the dots added between each component). 1743 const size_t available_chars = 1744 installer::kMaxAppModelIdLength - (components.size() - 1); 1745 const size_t max_component_length = available_chars / components.size(); 1746 1747 // |max_component_length| should be at least 2; otherwise the truncation logic 1748 // below breaks. 1749 if (max_component_length < 2U) { 1750 NOTREACHED(); 1751 return (*components.begin()).substr(0, installer::kMaxAppModelIdLength); 1752 } 1753 1754 base::string16 app_id; 1755 app_id.reserve(installer::kMaxAppModelIdLength); 1756 for (std::vector<base::string16>::const_iterator it = components.begin(); 1757 it != components.end(); ++it) { 1758 if (it != components.begin()) 1759 app_id.push_back(L'.'); 1760 1761 const base::string16& component = *it; 1762 DCHECK(!component.empty()); 1763 if (component.length() > max_component_length) { 1764 // Append a shortened version of this component. Cut in the middle to try 1765 // to avoid losing the unique parts of this component (which are usually 1766 // at the beginning or end for things like usernames and paths). 1767 app_id.append(component.c_str(), 0, max_component_length / 2); 1768 app_id.append(component.c_str(), 1769 component.length() - ((max_component_length + 1) / 2), 1770 base::string16::npos); 1771 } else { 1772 app_id.append(component); 1773 } 1774 } 1775 // No spaces are allowed in the AppUserModelId according to MSDN. 1776 base::ReplaceChars(app_id, base::ASCIIToUTF16(" "), base::ASCIIToUTF16("_"), 1777 &app_id); 1778 return app_id; 1779 } 1780 1781 ShellUtil::DefaultState ShellUtil::GetChromeDefaultState() { 1782 base::FilePath app_path; 1783 if (!PathService::Get(base::FILE_EXE, &app_path)) { 1784 NOTREACHED(); 1785 return ShellUtil::UNKNOWN_DEFAULT; 1786 } 1787 1788 return GetChromeDefaultStateFromPath(app_path); 1789 } 1790 1791 ShellUtil::DefaultState ShellUtil::GetChromeDefaultStateFromPath( 1792 const base::FilePath& chrome_exe) { 1793 BrowserDistribution* distribution = BrowserDistribution::GetDistribution(); 1794 if (distribution->GetDefaultBrowserControlPolicy() == 1795 BrowserDistribution::DEFAULT_BROWSER_UNSUPPORTED) { 1796 return NOT_DEFAULT; 1797 } 1798 // When we check for default browser we don't necessarily want to count file 1799 // type handlers and icons as having changed the default browser status, 1800 // since the user may have changed their shell settings to cause HTML files 1801 // to open with a text editor for example. We also don't want to aggressively 1802 // claim FTP, since the user may have a separate FTP client. It is an open 1803 // question as to how to "heal" these settings. Perhaps the user should just 1804 // re-run the installer or run with the --set-default-browser command line 1805 // flag. There is doubtless some other key we can hook into to cause "Repair" 1806 // to show up in Add/Remove programs for us. 1807 static const wchar_t* const kChromeProtocols[] = { L"http", L"https" }; 1808 return ProbeProtocolHandlers(chrome_exe, 1809 kChromeProtocols, 1810 arraysize(kChromeProtocols)); 1811 } 1812 1813 ShellUtil::DefaultState ShellUtil::GetChromeDefaultProtocolClientState( 1814 const base::string16& protocol) { 1815 BrowserDistribution* distribution = BrowserDistribution::GetDistribution(); 1816 if (distribution->GetDefaultBrowserControlPolicy() == 1817 BrowserDistribution::DEFAULT_BROWSER_UNSUPPORTED) { 1818 return NOT_DEFAULT; 1819 } 1820 1821 if (protocol.empty()) 1822 return UNKNOWN_DEFAULT; 1823 1824 base::FilePath chrome_exe; 1825 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { 1826 NOTREACHED(); 1827 return ShellUtil::UNKNOWN_DEFAULT; 1828 } 1829 1830 const wchar_t* const protocols[] = { protocol.c_str() }; 1831 return ProbeProtocolHandlers(chrome_exe, 1832 protocols, 1833 arraysize(protocols)); 1834 } 1835 1836 // static 1837 bool ShellUtil::CanMakeChromeDefaultUnattended() { 1838 return base::win::GetVersion() < base::win::VERSION_WIN8; 1839 } 1840 1841 bool ShellUtil::MakeChromeDefault(BrowserDistribution* dist, 1842 int shell_change, 1843 const base::string16& chrome_exe, 1844 bool elevate_if_not_admin) { 1845 DCHECK(!(shell_change & ShellUtil::SYSTEM_LEVEL) || IsUserAnAdmin()); 1846 1847 BrowserDistribution* distribution = BrowserDistribution::GetDistribution(); 1848 if (distribution->GetDefaultBrowserControlPolicy() != 1849 BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL) { 1850 return false; 1851 } 1852 1853 // Windows 8 does not permit making a browser default just like that. 1854 // This process needs to be routed through the system's UI. Use 1855 // ShowMakeChromeDefaultSystemUI instead (below). 1856 if (!CanMakeChromeDefaultUnattended()) { 1857 return false; 1858 } 1859 1860 if (!ShellUtil::RegisterChromeBrowser( 1861 dist, chrome_exe, base::string16(), elevate_if_not_admin)) { 1862 return false; 1863 } 1864 1865 bool ret = true; 1866 // First use the new "recommended" way on Vista to make Chrome default 1867 // browser. 1868 base::string16 app_name = GetApplicationName(dist, chrome_exe); 1869 1870 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { 1871 // On Windows Vista and Win7 we still can set ourselves via the 1872 // the IApplicationAssociationRegistration interface. 1873 VLOG(1) << "Registering Chrome as default browser on Vista."; 1874 base::win::ScopedComPtr<IApplicationAssociationRegistration> pAAR; 1875 HRESULT hr = pAAR.CreateInstance(CLSID_ApplicationAssociationRegistration, 1876 NULL, CLSCTX_INPROC); 1877 if (SUCCEEDED(hr)) { 1878 for (int i = 0; ShellUtil::kBrowserProtocolAssociations[i] != NULL; i++) { 1879 hr = pAAR->SetAppAsDefault(app_name.c_str(), 1880 ShellUtil::kBrowserProtocolAssociations[i], AT_URLPROTOCOL); 1881 if (!SUCCEEDED(hr)) { 1882 ret = false; 1883 LOG(ERROR) << "Failed to register as default for protocol " 1884 << ShellUtil::kBrowserProtocolAssociations[i] 1885 << " (" << hr << ")"; 1886 } 1887 } 1888 1889 for (int i = 0; ShellUtil::kDefaultFileAssociations[i] != NULL; i++) { 1890 hr = pAAR->SetAppAsDefault(app_name.c_str(), 1891 ShellUtil::kDefaultFileAssociations[i], AT_FILEEXTENSION); 1892 if (!SUCCEEDED(hr)) { 1893 ret = false; 1894 LOG(ERROR) << "Failed to register as default for file extension " 1895 << ShellUtil::kDefaultFileAssociations[i] 1896 << " (" << hr << ")"; 1897 } 1898 } 1899 } 1900 } 1901 1902 if (!RegisterChromeAsDefaultXPStyle(dist, shell_change, chrome_exe)) 1903 ret = false; 1904 1905 // Send Windows notification event so that it can update icons for 1906 // file associations. 1907 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL); 1908 return ret; 1909 } 1910 1911 bool ShellUtil::ShowMakeChromeDefaultSystemUI( 1912 BrowserDistribution* dist, 1913 const base::string16& chrome_exe) { 1914 DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8); 1915 if (dist->GetDefaultBrowserControlPolicy() != 1916 BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL) { 1917 return false; 1918 } 1919 1920 if (!RegisterChromeBrowser(dist, chrome_exe, base::string16(), true)) 1921 return false; 1922 1923 bool succeeded = true; 1924 bool is_default = (GetChromeDefaultState() == IS_DEFAULT); 1925 if (!is_default) { 1926 // On Windows 8, you can't set yourself as the default handler 1927 // programatically. In other words IApplicationAssociationRegistration 1928 // has been rendered useless. What you can do is to launch 1929 // "Set Program Associations" section of the "Default Programs" 1930 // control panel, which is a mess, or pop the concise "How you want to open 1931 // webpages?" dialog. We choose the latter. 1932 succeeded = LaunchSelectDefaultProtocolHandlerDialog(L"http"); 1933 is_default = (succeeded && GetChromeDefaultState() == IS_DEFAULT); 1934 } 1935 if (succeeded && is_default) 1936 RegisterChromeAsDefaultXPStyle(dist, CURRENT_USER, chrome_exe); 1937 return succeeded; 1938 } 1939 1940 bool ShellUtil::MakeChromeDefaultProtocolClient( 1941 BrowserDistribution* dist, 1942 const base::string16& chrome_exe, 1943 const base::string16& protocol) { 1944 if (dist->GetDefaultBrowserControlPolicy() != 1945 BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL) { 1946 return false; 1947 } 1948 1949 if (!RegisterChromeForProtocol( 1950 dist, chrome_exe, base::string16(), protocol, true)) 1951 return false; 1952 1953 // Windows 8 does not permit making a browser default just like that. 1954 // This process needs to be routed through the system's UI. Use 1955 // ShowMakeChromeDefaultProocolClientSystemUI instead (below). 1956 if (!CanMakeChromeDefaultUnattended()) 1957 return false; 1958 1959 bool ret = true; 1960 // First use the new "recommended" way on Vista to make Chrome default 1961 // protocol handler. 1962 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { 1963 VLOG(1) << "Registering Chrome as default handler for " << protocol 1964 << " on Vista."; 1965 base::win::ScopedComPtr<IApplicationAssociationRegistration> pAAR; 1966 HRESULT hr = pAAR.CreateInstance(CLSID_ApplicationAssociationRegistration, 1967 NULL, CLSCTX_INPROC); 1968 if (SUCCEEDED(hr)) { 1969 base::string16 app_name = GetApplicationName(dist, chrome_exe); 1970 hr = pAAR->SetAppAsDefault(app_name.c_str(), protocol.c_str(), 1971 AT_URLPROTOCOL); 1972 } 1973 if (!SUCCEEDED(hr)) { 1974 ret = false; 1975 LOG(ERROR) << "Could not make Chrome default protocol client (Vista):" 1976 << " HRESULT=" << hr << "."; 1977 } 1978 } 1979 1980 // Now use the old way to associate Chrome with the desired protocol. This 1981 // should not be required on Vista+, but since some applications still read 1982 // Software\Classes\<protocol> key directly, do this on Vista+ also. 1983 if (!RegisterChromeAsDefaultProtocolClientXPStyle(dist, chrome_exe, protocol)) 1984 ret = false; 1985 1986 return ret; 1987 } 1988 1989 bool ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI( 1990 BrowserDistribution* dist, 1991 const base::string16& chrome_exe, 1992 const base::string16& protocol) { 1993 DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8); 1994 if (dist->GetDefaultBrowserControlPolicy() != 1995 BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL) { 1996 return false; 1997 } 1998 1999 if (!RegisterChromeForProtocol( 2000 dist, chrome_exe, base::string16(), protocol, true)) 2001 return false; 2002 2003 bool succeeded = true; 2004 bool is_default = ( 2005 GetChromeDefaultProtocolClientState(protocol) == IS_DEFAULT); 2006 if (!is_default) { 2007 // On Windows 8, you can't set yourself as the default handler 2008 // programatically. In other words IApplicationAssociationRegistration 2009 // has been rendered useless. What you can do is to launch 2010 // "Set Program Associations" section of the "Default Programs" 2011 // control panel, which is a mess, or pop the concise "How you want to open 2012 // links of this type (protocol)?" dialog. We choose the latter. 2013 succeeded = LaunchSelectDefaultProtocolHandlerDialog(protocol.c_str()); 2014 is_default = (succeeded && 2015 GetChromeDefaultProtocolClientState(protocol) == IS_DEFAULT); 2016 } 2017 if (succeeded && is_default) 2018 RegisterChromeAsDefaultProtocolClientXPStyle(dist, chrome_exe, protocol); 2019 return succeeded; 2020 } 2021 2022 bool ShellUtil::RegisterChromeBrowser(BrowserDistribution* dist, 2023 const base::string16& chrome_exe, 2024 const base::string16& unique_suffix, 2025 bool elevate_if_not_admin) { 2026 if (dist->GetDefaultBrowserControlPolicy() == 2027 BrowserDistribution::DEFAULT_BROWSER_UNSUPPORTED) { 2028 return false; 2029 } 2030 2031 CommandLine& command_line = *CommandLine::ForCurrentProcess(); 2032 2033 base::string16 suffix; 2034 if (!unique_suffix.empty()) { 2035 suffix = unique_suffix; 2036 } else if (command_line.HasSwitch( 2037 installer::switches::kRegisterChromeBrowserSuffix)) { 2038 suffix = command_line.GetSwitchValueNative( 2039 installer::switches::kRegisterChromeBrowserSuffix); 2040 } else if (!GetInstallationSpecificSuffix(dist, chrome_exe, &suffix)) { 2041 return false; 2042 } 2043 2044 RemoveRunVerbOnWindows8(dist, chrome_exe); 2045 2046 bool user_level = InstallUtil::IsPerUserInstall(chrome_exe.c_str()); 2047 HKEY root = DetermineRegistrationRoot(user_level); 2048 2049 // Look only in HKLM for system-level installs (otherwise, if a user-level 2050 // install is also present, it will lead IsChromeRegistered() to think this 2051 // system-level install isn't registered properly as it is shadowed by the 2052 // user-level install's registrations). 2053 uint32 look_for_in = user_level ? 2054 RegistryEntry::LOOK_IN_HKCU_THEN_HKLM : RegistryEntry::LOOK_IN_HKLM; 2055 2056 // Check if chrome is already registered with this suffix. 2057 if (IsChromeRegistered(dist, chrome_exe, suffix, look_for_in)) 2058 return true; 2059 2060 bool result = true; 2061 if (root == HKEY_CURRENT_USER || IsUserAnAdmin()) { 2062 // Do the full registration if we can do it at user-level or if the user is 2063 // an admin. 2064 ScopedVector<RegistryEntry> progid_and_appreg_entries; 2065 ScopedVector<RegistryEntry> shell_entries; 2066 RegistryEntry::GetProgIdEntries(dist, chrome_exe, suffix, 2067 &progid_and_appreg_entries); 2068 RegistryEntry::GetAppRegistrationEntries(chrome_exe, suffix, 2069 &progid_and_appreg_entries); 2070 RegistryEntry::GetShellIntegrationEntries( 2071 dist, chrome_exe, suffix, &shell_entries); 2072 result = (AddRegistryEntries(root, progid_and_appreg_entries) && 2073 AddRegistryEntries(root, shell_entries)); 2074 } else if (elevate_if_not_admin && 2075 base::win::GetVersion() >= base::win::VERSION_VISTA && 2076 ElevateAndRegisterChrome(dist, chrome_exe, suffix, base::string16())) { 2077 // If the user is not an admin and OS is between Vista and Windows 7 2078 // inclusively, try to elevate and register. This is only intended for 2079 // user-level installs as system-level installs should always be run with 2080 // admin rights. 2081 result = true; 2082 } else { 2083 // If we got to this point then all we can do is create ProgId and basic app 2084 // registrations under HKCU. 2085 ScopedVector<RegistryEntry> entries; 2086 RegistryEntry::GetProgIdEntries( 2087 dist, chrome_exe, base::string16(), &entries); 2088 // Prefer to use |suffix|; unless Chrome's ProgIds are already registered 2089 // with no suffix (as per the old registration style): in which case some 2090 // other registry entries could refer to them and since we were not able to 2091 // set our HKLM entries above, we are better off not altering these here. 2092 if (!AreEntriesRegistered(entries, RegistryEntry::LOOK_IN_HKCU)) { 2093 if (!suffix.empty()) { 2094 entries.clear(); 2095 RegistryEntry::GetProgIdEntries(dist, chrome_exe, suffix, &entries); 2096 RegistryEntry::GetAppRegistrationEntries(chrome_exe, suffix, &entries); 2097 } 2098 result = AddRegistryEntries(HKEY_CURRENT_USER, entries); 2099 } else { 2100 // The ProgId is registered unsuffixed in HKCU, also register the app with 2101 // Windows in HKCU (this was not done in the old registration style and 2102 // thus needs to be done after the above check for the unsuffixed 2103 // registration). 2104 entries.clear(); 2105 RegistryEntry::GetAppRegistrationEntries(chrome_exe, base::string16(), 2106 &entries); 2107 result = AddRegistryEntries(HKEY_CURRENT_USER, entries); 2108 } 2109 } 2110 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL); 2111 return result; 2112 } 2113 2114 bool ShellUtil::RegisterChromeForProtocol(BrowserDistribution* dist, 2115 const base::string16& chrome_exe, 2116 const base::string16& unique_suffix, 2117 const base::string16& protocol, 2118 bool elevate_if_not_admin) { 2119 if (dist->GetDefaultBrowserControlPolicy() == 2120 BrowserDistribution::DEFAULT_BROWSER_UNSUPPORTED) { 2121 return false; 2122 } 2123 2124 base::string16 suffix; 2125 if (!unique_suffix.empty()) { 2126 suffix = unique_suffix; 2127 } else if (!GetInstallationSpecificSuffix(dist, chrome_exe, &suffix)) { 2128 return false; 2129 } 2130 2131 bool user_level = InstallUtil::IsPerUserInstall(chrome_exe.c_str()); 2132 HKEY root = DetermineRegistrationRoot(user_level); 2133 2134 // Look only in HKLM for system-level installs (otherwise, if a user-level 2135 // install is also present, it could lead IsChromeRegisteredForProtocol() to 2136 // think this system-level install isn't registered properly as it may be 2137 // shadowed by the user-level install's registrations). 2138 uint32 look_for_in = user_level ? 2139 RegistryEntry::LOOK_IN_HKCU_THEN_HKLM : RegistryEntry::LOOK_IN_HKLM; 2140 2141 // Check if chrome is already registered with this suffix. 2142 if (IsChromeRegisteredForProtocol(dist, suffix, protocol, look_for_in)) 2143 return true; 2144 2145 if (root == HKEY_CURRENT_USER || IsUserAnAdmin()) { 2146 // We can do this operation directly. 2147 // First, make sure Chrome is fully registered on this machine. 2148 if (!RegisterChromeBrowser(dist, chrome_exe, suffix, false)) 2149 return false; 2150 2151 // Write in the capabillity for the protocol. 2152 ScopedVector<RegistryEntry> entries; 2153 RegistryEntry::GetProtocolCapabilityEntries(dist, suffix, protocol, 2154 &entries); 2155 return AddRegistryEntries(root, entries); 2156 } else if (elevate_if_not_admin && 2157 base::win::GetVersion() >= base::win::VERSION_VISTA) { 2158 // Elevate to do the whole job 2159 return ElevateAndRegisterChrome(dist, chrome_exe, suffix, protocol); 2160 } else { 2161 // Admin rights are required to register capabilities before Windows 8. 2162 return false; 2163 } 2164 } 2165 2166 // static 2167 bool ShellUtil::RemoveShortcuts(ShellUtil::ShortcutLocation location, 2168 BrowserDistribution* dist, 2169 ShellChange level, 2170 const base::FilePath& target_exe) { 2171 if (!ShellUtil::ShortcutLocationIsSupported(location)) 2172 return true; // Vacuous success. 2173 2174 FilterTargetEq shortcut_filter(target_exe, false); 2175 // Main operation to apply to each shortcut in the directory specified. 2176 ShortcutOperationCallback shortcut_operation( 2177 location == SHORTCUT_LOCATION_TASKBAR_PINS ? 2178 base::Bind(&ShortcutOpUnpin) : base::Bind(&ShortcutOpDelete)); 2179 bool success = BatchShortcutAction(shortcut_filter.AsShortcutFilterCallback(), 2180 shortcut_operation, location, dist, level, 2181 NULL); 2182 // Remove chrome-specific shortcut folders if they are now empty. 2183 if (success && 2184 (location == SHORTCUT_LOCATION_START_MENU_CHROME_DIR || 2185 location == SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR || 2186 location == SHORTCUT_LOCATION_APP_SHORTCUTS)) { 2187 success = RemoveShortcutFolderIfEmpty(location, dist, level); 2188 } 2189 return success; 2190 } 2191 2192 // static 2193 bool ShellUtil::RetargetShortcutsWithArgs( 2194 ShellUtil::ShortcutLocation location, 2195 BrowserDistribution* dist, 2196 ShellChange level, 2197 const base::FilePath& old_target_exe, 2198 const base::FilePath& new_target_exe) { 2199 if (!ShellUtil::ShortcutLocationIsSupported(location)) 2200 return true; // Vacuous success. 2201 2202 FilterTargetEq shortcut_filter(old_target_exe, true); 2203 ShortcutOperationCallback shortcut_operation( 2204 base::Bind(&ShortcutOpRetarget, old_target_exe, new_target_exe)); 2205 return BatchShortcutAction(shortcut_filter.AsShortcutFilterCallback(), 2206 shortcut_operation, location, dist, level, NULL); 2207 } 2208 2209 // static 2210 bool ShellUtil::ShortcutListMaybeRemoveUnknownArgs( 2211 ShellUtil::ShortcutLocation location, 2212 BrowserDistribution* dist, 2213 ShellChange level, 2214 const base::FilePath& chrome_exe, 2215 bool do_removal, 2216 const scoped_refptr<SharedCancellationFlag>& cancel, 2217 std::vector<std::pair<base::FilePath, base::string16> >* shortcuts) { 2218 if (!ShellUtil::ShortcutLocationIsSupported(location)) 2219 return false; 2220 DCHECK(dist); 2221 FilterTargetEq shortcut_filter(chrome_exe, true); 2222 ShortcutOperationCallback shortcut_operation( 2223 base::Bind(&ShortcutOpListOrRemoveUnknownArgs, do_removal, shortcuts)); 2224 return BatchShortcutAction(shortcut_filter.AsShortcutFilterCallback(), 2225 shortcut_operation, location, dist, level, cancel); 2226 } 2227 2228 bool ShellUtil::GetUserSpecificRegistrySuffix(base::string16* suffix) { 2229 // Use a thread-safe cache for the user's suffix. 2230 static base::LazyInstance<UserSpecificRegistrySuffix>::Leaky suffix_instance = 2231 LAZY_INSTANCE_INITIALIZER; 2232 return suffix_instance.Get().GetSuffix(suffix); 2233 } 2234 2235 bool ShellUtil::GetOldUserSpecificRegistrySuffix(base::string16* suffix) { 2236 wchar_t user_name[256]; 2237 DWORD size = arraysize(user_name); 2238 if (::GetUserName(user_name, &size) == 0 || size < 1) { 2239 NOTREACHED(); 2240 return false; 2241 } 2242 suffix->reserve(size); 2243 suffix->assign(1, L'.'); 2244 suffix->append(user_name, size - 1); 2245 return true; 2246 } 2247 2248 base::string16 ShellUtil::ByteArrayToBase32(const uint8* bytes, size_t size) { 2249 static const char kEncoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; 2250 2251 // Eliminate special cases first. 2252 if (size == 0) { 2253 return base::string16(); 2254 } else if (size == 1) { 2255 base::string16 ret; 2256 ret.push_back(kEncoding[(bytes[0] & 0xf8) >> 3]); 2257 ret.push_back(kEncoding[(bytes[0] & 0x07) << 2]); 2258 return ret; 2259 } else if (size >= std::numeric_limits<size_t>::max() / 8) { 2260 // If |size| is too big, the calculation of |encoded_length| below will 2261 // overflow. 2262 NOTREACHED(); 2263 return base::string16(); 2264 } 2265 2266 // Overestimate the number of bits in the string by 4 so that dividing by 5 2267 // is the equivalent of rounding up the actual number of bits divided by 5. 2268 const size_t encoded_length = (size * 8 + 4) / 5; 2269 2270 base::string16 ret; 2271 ret.reserve(encoded_length); 2272 2273 // A bit stream which will be read from the left and appended to from the 2274 // right as it's emptied. 2275 uint16 bit_stream = (bytes[0] << 8) + bytes[1]; 2276 size_t next_byte_index = 2; 2277 int free_bits = 0; 2278 while (free_bits < 16) { 2279 // Extract the 5 leftmost bits in the stream 2280 ret.push_back(kEncoding[(bit_stream & 0xf800) >> 11]); 2281 bit_stream <<= 5; 2282 free_bits += 5; 2283 2284 // If there is enough room in the bit stream, inject another byte (if there 2285 // are any left...). 2286 if (free_bits >= 8 && next_byte_index < size) { 2287 free_bits -= 8; 2288 bit_stream += bytes[next_byte_index++] << free_bits; 2289 } 2290 } 2291 2292 DCHECK_EQ(ret.length(), encoded_length); 2293 return ret; 2294 } 2295