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