shell_util.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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 52using base::win::RegKey; 53 54namespace { 55 56// An enum used to tell QuickIsChromeRegistered() which level of registration 57// the caller wants to confirm. 58enum 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 74const 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. 79bool 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. 104string16 GetBrowserProgId(const string16& suffix) { 105 string16 chrome_html(ShellUtil::kChromeHTMLProgId); 106 chrome_html.append(suffix); 107 108 // ProgIds cannot be longer than 39 characters. 109 // Ref: http://msdn.microsoft.com/en-us/library/aa911706.aspx. 110 // Make all new registrations comply with this requirement (existing 111 // registrations must be preserved). 112 string16 new_style_suffix; 113 if (ShellUtil::GetUserSpecificRegistrySuffix(&new_style_suffix) && 114 suffix == new_style_suffix && chrome_html.length() > 39) { 115 NOTREACHED(); 116 chrome_html.erase(39); 117 } 118 return chrome_html; 119} 120 121// This class is used to initialize and cache a base 32 encoding of the md5 hash 122// of this user's sid preceded by a dot. 123// This is guaranteed to be unique on the machine and 27 characters long 124// (including the '.'). 125// This is then meant to be used as a suffix on all registrations that may 126// conflict with another user-level Chrome install. 127class UserSpecificRegistrySuffix { 128 public: 129 // All the initialization is done in the constructor to be able to build the 130 // suffix in a thread-safe manner when used in conjunction with a 131 // LazyInstance. 132 UserSpecificRegistrySuffix(); 133 134 // Sets |suffix| to the pre-computed suffix cached in this object. 135 // Returns true unless the initialization originally failed. 136 bool GetSuffix(string16* suffix); 137 138 private: 139 string16 suffix_; 140 141 DISALLOW_COPY_AND_ASSIGN(UserSpecificRegistrySuffix); 142}; // class UserSpecificRegistrySuffix 143 144UserSpecificRegistrySuffix::UserSpecificRegistrySuffix() { 145 string16 user_sid; 146 if (!base::win::GetUserSidString(&user_sid)) { 147 NOTREACHED(); 148 return; 149 } 150 COMPILE_ASSERT(sizeof(base::MD5Digest) == 16, size_of_MD5_not_as_expected_); 151 base::MD5Digest md5_digest; 152 std::string user_sid_ascii(UTF16ToASCII(user_sid)); 153 base::MD5Sum(user_sid_ascii.c_str(), user_sid_ascii.length(), &md5_digest); 154 const string16 base32_md5( 155 ShellUtil::ByteArrayToBase32(md5_digest.a, arraysize(md5_digest.a))); 156 // The value returned by the base32 algorithm above must never change and 157 // must always be 26 characters long (i.e. if someone ever moves this to 158 // base and implements the full base32 algorithm (i.e. with appended '=' 159 // signs in the output), they must provide a flag to allow this method to 160 // still request the output with no appended '=' signs). 161 DCHECK_EQ(base32_md5.length(), 26U); 162 suffix_.reserve(base32_md5.length() + 1); 163 suffix_.assign(1, L'.'); 164 suffix_.append(base32_md5); 165} 166 167bool UserSpecificRegistrySuffix::GetSuffix(string16* suffix) { 168 if (suffix_.empty()) { 169 NOTREACHED(); 170 return false; 171 } 172 suffix->assign(suffix_); 173 return true; 174} 175 176// This class represents a single registry entry. The objective is to 177// encapsulate all the registry entries required for registering Chrome at one 178// place. This class can not be instantiated outside the class and the objects 179// of this class type can be obtained only by calling a static method of this 180// class. 181class RegistryEntry { 182 public: 183 // A bit-field enum of places to look for this key in the Windows registry. 184 enum LookForIn { 185 LOOK_IN_HKCU = 1 << 0, 186 LOOK_IN_HKLM = 1 << 1, 187 LOOK_IN_HKCU_THEN_HKLM = LOOK_IN_HKCU | LOOK_IN_HKLM, 188 }; 189 190 // Returns the Windows browser client registration key for Chrome. For 191 // example: "Software\Clients\StartMenuInternet\Chromium[.user]". Strictly 192 // speaking, we should use the name of the executable (e.g., "chrome.exe"), 193 // but that ship has sailed. The cost of switching now is re-prompting users 194 // to make Chrome their default browser, which isn't polite. |suffix| is the 195 // user-specific registration suffix; see GetUserSpecificDefaultBrowserSuffix 196 // in shell_util.h for details. 197 static string16 GetBrowserClientKey(BrowserDistribution* dist, 198 const string16& suffix) { 199 DCHECK(suffix.empty() || suffix[0] == L'.'); 200 return string16(ShellUtil::kRegStartMenuInternet) 201 .append(1, L'\\') 202 .append(dist->GetBaseAppName()) 203 .append(suffix); 204 } 205 206 // Returns the Windows Default Programs capabilities key for Chrome. For 207 // example: 208 // "Software\Clients\StartMenuInternet\Chromium[.user]\Capabilities". 209 static string16 GetCapabilitiesKey(BrowserDistribution* dist, 210 const string16& suffix) { 211 return GetBrowserClientKey(dist, suffix).append(L"\\Capabilities"); 212 } 213 214 // This method returns a list of all the registry entries that 215 // are needed to register this installation's ProgId and AppId. 216 // These entries need to be registered in HKLM prior to Win8. 217 static void GetProgIdEntries(BrowserDistribution* dist, 218 const string16& chrome_exe, 219 const string16& suffix, 220 ScopedVector<RegistryEntry>* entries) { 221 string16 icon_path( 222 ShellUtil::FormatIconLocation( 223 chrome_exe, 224 dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME))); 225 string16 open_cmd(ShellUtil::GetChromeShellOpenCmd(chrome_exe)); 226 string16 delegate_command(ShellUtil::GetChromeDelegateCommand(chrome_exe)); 227 // For user-level installs: entries for the app id and DelegateExecute verb 228 // handler will be in HKCU; thus we do not need a suffix on those entries. 229 string16 app_id( 230 ShellUtil::GetBrowserModelId( 231 dist, InstallUtil::IsPerUserInstall(chrome_exe.c_str()))); 232 string16 delegate_guid; 233 bool set_delegate_execute = 234 IsChromeMetroSupported() && 235 dist->GetCommandExecuteImplClsid(&delegate_guid); 236 237 // DelegateExecute ProgId. Needed for Chrome Metro in Windows 8. 238 if (set_delegate_execute) { 239 string16 model_id_shell(ShellUtil::kRegClasses); 240 model_id_shell.push_back(base::FilePath::kSeparators[0]); 241 model_id_shell.append(app_id); 242 model_id_shell.append(ShellUtil::kRegExePath); 243 model_id_shell.append(ShellUtil::kRegShellPath); 244 245 // <root hkey>\Software\Classes\<app_id>\.exe\shell @=open 246 entries->push_back(new RegistryEntry(model_id_shell, 247 ShellUtil::kRegVerbOpen)); 248 249 // Each of Chrome's shortcuts has an appid; which, as of Windows 8, is 250 // registered to handle some verbs. This registration has the side-effect 251 // that these verbs now show up in the shortcut's context menu. We 252 // mitigate this side-effect by making the context menu entries 253 // user readable/localized strings. See relevant MSDN article: 254 // http://msdn.microsoft.com/en-US/library/windows/desktop/cc144171.aspx 255 const struct { 256 const wchar_t* verb; 257 int name_id; 258 } verbs[] = { 259 { ShellUtil::kRegVerbOpen, -1 }, 260 { ShellUtil::kRegVerbOpenNewWindow, IDS_SHORTCUT_NEW_WINDOW_BASE }, 261 }; 262 for (size_t i = 0; i < arraysize(verbs); ++i) { 263 string16 sub_path(model_id_shell); 264 sub_path.push_back(base::FilePath::kSeparators[0]); 265 sub_path.append(verbs[i].verb); 266 267 // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb> 268 if (verbs[i].name_id != -1) { 269 // TODO(grt): http://crbug.com/75152 Write a reference to a localized 270 // resource. 271 string16 verb_name(installer::GetLocalizedString(verbs[i].name_id)); 272 entries->push_back(new RegistryEntry(sub_path, verb_name.c_str())); 273 } 274 entries->push_back(new RegistryEntry( 275 sub_path, L"CommandId", L"Browser.Launch")); 276 277 sub_path.push_back(base::FilePath::kSeparators[0]); 278 sub_path.append(ShellUtil::kRegCommand); 279 280 // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb>\command 281 entries->push_back(new RegistryEntry(sub_path, delegate_command)); 282 entries->push_back(new RegistryEntry( 283 sub_path, ShellUtil::kRegDelegateExecute, delegate_guid)); 284 } 285 } 286 287 // File association ProgId 288 string16 chrome_html_prog_id(ShellUtil::kRegClasses); 289 chrome_html_prog_id.push_back(base::FilePath::kSeparators[0]); 290 chrome_html_prog_id.append(GetBrowserProgId(suffix)); 291 entries->push_back(new RegistryEntry( 292 chrome_html_prog_id, ShellUtil::kChromeHTMLProgIdDesc)); 293 entries->push_back(new RegistryEntry( 294 chrome_html_prog_id, ShellUtil::kRegUrlProtocol, L"")); 295 entries->push_back(new RegistryEntry( 296 chrome_html_prog_id + ShellUtil::kRegDefaultIcon, icon_path)); 297 entries->push_back(new RegistryEntry( 298 chrome_html_prog_id + ShellUtil::kRegShellOpen, open_cmd)); 299 if (set_delegate_execute) { 300 entries->push_back(new RegistryEntry( 301 chrome_html_prog_id + ShellUtil::kRegShellOpen, 302 ShellUtil::kRegDelegateExecute, delegate_guid)); 303 } 304 305 // The following entries are required as of Windows 8, but do not 306 // depend on the DelegateExecute verb handler being set. 307 if (base::win::GetVersion() >= base::win::VERSION_WIN8) { 308 entries->push_back(new RegistryEntry( 309 chrome_html_prog_id, ShellUtil::kRegAppUserModelId, app_id)); 310 311 // Add \Software\Classes\ChromeHTML\Application entries 312 string16 chrome_application(chrome_html_prog_id + 313 ShellUtil::kRegApplication); 314 entries->push_back(new RegistryEntry( 315 chrome_application, ShellUtil::kRegAppUserModelId, app_id)); 316 entries->push_back(new RegistryEntry( 317 chrome_application, ShellUtil::kRegApplicationIcon, icon_path)); 318 // TODO(grt): http://crbug.com/75152 Write a reference to a localized 319 // resource for name, description, and company. 320 entries->push_back(new RegistryEntry( 321 chrome_application, ShellUtil::kRegApplicationName, 322 dist->GetDisplayName())); 323 entries->push_back(new RegistryEntry( 324 chrome_application, ShellUtil::kRegApplicationDescription, 325 dist->GetAppDescription())); 326 entries->push_back(new RegistryEntry( 327 chrome_application, ShellUtil::kRegApplicationCompany, 328 dist->GetPublisherName())); 329 } 330 } 331 332 // This method returns a list of the registry entries needed to declare a 333 // capability of handling a protocol on Windows. 334 static void GetProtocolCapabilityEntries( 335 BrowserDistribution* dist, 336 const string16& suffix, 337 const string16& protocol, 338 ScopedVector<RegistryEntry>* entries) { 339 entries->push_back(new RegistryEntry( 340 GetCapabilitiesKey(dist, suffix).append(L"\\URLAssociations"), 341 protocol, GetBrowserProgId(suffix))); 342 } 343 344 // This method returns a list of the registry entries required to register 345 // this installation in "RegisteredApplications" on Windows (to appear in 346 // Default Programs, StartMenuInternet, etc.). 347 // These entries need to be registered in HKLM prior to Win8. 348 // If |suffix| is not empty, these entries are guaranteed to be unique on this 349 // machine. 350 static void GetShellIntegrationEntries(BrowserDistribution* dist, 351 const string16& chrome_exe, 352 const string16& suffix, 353 ScopedVector<RegistryEntry>* entries) { 354 const string16 icon_path( 355 ShellUtil::FormatIconLocation( 356 chrome_exe, 357 dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME))); 358 const string16 quoted_exe_path(L"\"" + chrome_exe + L"\""); 359 360 // Register for the Start Menu "Internet" link (pre-Win7). 361 const string16 start_menu_entry(GetBrowserClientKey(dist, suffix)); 362 // Register Chrome's display name. 363 // TODO(grt): http://crbug.com/75152 Also set LocalizedString; see 364 // http://msdn.microsoft.com/en-us/library/windows/desktop/cc144109(v=VS.85).aspx#registering_the_display_name 365 entries->push_back(new RegistryEntry( 366 start_menu_entry, dist->GetDisplayName())); 367 // Register the "open" verb for launching Chrome via the "Internet" link. 368 entries->push_back(new RegistryEntry( 369 start_menu_entry + ShellUtil::kRegShellOpen, quoted_exe_path)); 370 // Register Chrome's icon for the Start Menu "Internet" link. 371 entries->push_back(new RegistryEntry( 372 start_menu_entry + ShellUtil::kRegDefaultIcon, icon_path)); 373 374 // Register installation information. 375 string16 install_info(start_menu_entry + L"\\InstallInfo"); 376 // Note: not using CommandLine since it has ambiguous rules for quoting 377 // strings. 378 entries->push_back(new RegistryEntry(install_info, kReinstallCommand, 379 quoted_exe_path + L" --" + ASCIIToWide(switches::kMakeDefaultBrowser))); 380 entries->push_back(new RegistryEntry(install_info, L"HideIconsCommand", 381 quoted_exe_path + L" --" + ASCIIToWide(switches::kHideIcons))); 382 entries->push_back(new RegistryEntry(install_info, L"ShowIconsCommand", 383 quoted_exe_path + L" --" + ASCIIToWide(switches::kShowIcons))); 384 entries->push_back(new RegistryEntry(install_info, L"IconsVisible", 1)); 385 386 // Register with Default Programs. 387 const string16 reg_app_name(dist->GetBaseAppName().append(suffix)); 388 // Tell Windows where to find Chrome's Default Programs info. 389 const string16 capabilities(GetCapabilitiesKey(dist, suffix)); 390 entries->push_back(new RegistryEntry(ShellUtil::kRegRegisteredApplications, 391 reg_app_name, capabilities)); 392 // Write out Chrome's Default Programs info. 393 // TODO(grt): http://crbug.com/75152 Write a reference to a localized 394 // resource rather than this. 395 entries->push_back(new RegistryEntry( 396 capabilities, ShellUtil::kRegApplicationDescription, 397 dist->GetLongAppDescription())); 398 entries->push_back(new RegistryEntry( 399 capabilities, ShellUtil::kRegApplicationIcon, icon_path)); 400 entries->push_back(new RegistryEntry( 401 capabilities, ShellUtil::kRegApplicationName, 402 dist->GetDisplayName())); 403 404 entries->push_back(new RegistryEntry(capabilities + L"\\Startmenu", 405 L"StartMenuInternet", reg_app_name)); 406 407 const string16 html_prog_id(GetBrowserProgId(suffix)); 408 for (int i = 0; ShellUtil::kPotentialFileAssociations[i] != NULL; i++) { 409 entries->push_back(new RegistryEntry( 410 capabilities + L"\\FileAssociations", 411 ShellUtil::kPotentialFileAssociations[i], html_prog_id)); 412 } 413 for (int i = 0; ShellUtil::kPotentialProtocolAssociations[i] != NULL; 414 i++) { 415 entries->push_back(new RegistryEntry( 416 capabilities + L"\\URLAssociations", 417 ShellUtil::kPotentialProtocolAssociations[i], html_prog_id)); 418 } 419 } 420 421 // This method returns a list of the registry entries required for this 422 // installation to be registered in the Windows shell. 423 // In particular: 424 // - App Paths 425 // http://msdn.microsoft.com/en-us/library/windows/desktop/ee872121 426 // - File Associations 427 // http://msdn.microsoft.com/en-us/library/bb166549 428 // These entries need to be registered in HKLM prior to Win8. 429 static void GetAppRegistrationEntries(const string16& chrome_exe, 430 const string16& suffix, 431 ScopedVector<RegistryEntry>* entries) { 432 const base::FilePath chrome_path(chrome_exe); 433 string16 app_path_key(ShellUtil::kAppPathsRegistryKey); 434 app_path_key.push_back(base::FilePath::kSeparators[0]); 435 app_path_key.append(chrome_path.BaseName().value()); 436 entries->push_back(new RegistryEntry(app_path_key, chrome_exe)); 437 entries->push_back(new RegistryEntry(app_path_key, 438 ShellUtil::kAppPathsRegistryPathName, chrome_path.DirName().value())); 439 440 const string16 html_prog_id(GetBrowserProgId(suffix)); 441 for (int i = 0; ShellUtil::kPotentialFileAssociations[i] != NULL; i++) { 442 string16 key(ShellUtil::kRegClasses); 443 key.push_back(base::FilePath::kSeparators[0]); 444 key.append(ShellUtil::kPotentialFileAssociations[i]); 445 key.push_back(base::FilePath::kSeparators[0]); 446 key.append(ShellUtil::kRegOpenWithProgids); 447 entries->push_back(new RegistryEntry(key, html_prog_id, string16())); 448 } 449 } 450 451 // This method returns a list of all the user level registry entries that 452 // are needed to make Chromium the default handler for a protocol on XP. 453 static void GetXPStyleUserProtocolEntries( 454 const string16& protocol, 455 const string16& chrome_icon, 456 const string16& chrome_open, 457 ScopedVector<RegistryEntry>* entries) { 458 // Protocols associations. 459 string16 url_key(ShellUtil::kRegClasses); 460 url_key.push_back(base::FilePath::kSeparators[0]); 461 url_key.append(protocol); 462 463 // This registry value tells Windows that this 'class' is a URL scheme 464 // so IE, explorer and other apps will route it to our handler. 465 // <root hkey>\Software\Classes\<protocol>\URL Protocol 466 entries->push_back(new RegistryEntry(url_key, 467 ShellUtil::kRegUrlProtocol, L"")); 468 469 // <root hkey>\Software\Classes\<protocol>\DefaultIcon 470 string16 icon_key = url_key + ShellUtil::kRegDefaultIcon; 471 entries->push_back(new RegistryEntry(icon_key, chrome_icon)); 472 473 // <root hkey>\Software\Classes\<protocol>\shell\open\command 474 string16 shell_key = url_key + ShellUtil::kRegShellOpen; 475 entries->push_back(new RegistryEntry(shell_key, chrome_open)); 476 477 // <root hkey>\Software\Classes\<protocol>\shell\open\ddeexec 478 string16 dde_key = url_key + L"\\shell\\open\\ddeexec"; 479 entries->push_back(new RegistryEntry(dde_key, L"")); 480 481 // <root hkey>\Software\Classes\<protocol>\shell\@ 482 string16 protocol_shell_key = url_key + ShellUtil::kRegShellPath; 483 entries->push_back(new RegistryEntry(protocol_shell_key, L"open")); 484 } 485 486 // This method returns a list of all the user level registry entries that 487 // are needed to make Chromium default browser on XP. 488 // Some of these entries are irrelevant in recent versions of Windows, but 489 // we register them anyways as some legacy apps are hardcoded to lookup those 490 // values. 491 static void GetXPStyleDefaultBrowserUserEntries( 492 BrowserDistribution* dist, 493 const string16& chrome_exe, 494 const string16& suffix, 495 ScopedVector<RegistryEntry>* entries) { 496 // File extension associations. 497 string16 html_prog_id(GetBrowserProgId(suffix)); 498 for (int i = 0; ShellUtil::kDefaultFileAssociations[i] != NULL; i++) { 499 string16 ext_key(ShellUtil::kRegClasses); 500 ext_key.push_back(base::FilePath::kSeparators[0]); 501 ext_key.append(ShellUtil::kDefaultFileAssociations[i]); 502 entries->push_back(new RegistryEntry(ext_key, html_prog_id)); 503 } 504 505 // Protocols associations. 506 string16 chrome_open = ShellUtil::GetChromeShellOpenCmd(chrome_exe); 507 string16 chrome_icon = 508 ShellUtil::FormatIconLocation( 509 chrome_exe, 510 dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME)); 511 for (int i = 0; ShellUtil::kBrowserProtocolAssociations[i] != NULL; i++) { 512 GetXPStyleUserProtocolEntries(ShellUtil::kBrowserProtocolAssociations[i], 513 chrome_icon, chrome_open, entries); 514 } 515 516 // start->Internet shortcut. 517 string16 start_menu(ShellUtil::kRegStartMenuInternet); 518 string16 app_name = dist->GetBaseAppName() + suffix; 519 entries->push_back(new RegistryEntry(start_menu, app_name)); 520 } 521 522 // Generate work_item tasks required to create current registry entry and 523 // add them to the given work item list. 524 void AddToWorkItemList(HKEY root, WorkItemList *items) const { 525 items->AddCreateRegKeyWorkItem(root, key_path_); 526 if (is_string_) { 527 items->AddSetRegValueWorkItem(root, key_path_, name_, value_, true); 528 } else { 529 items->AddSetRegValueWorkItem(root, key_path_, name_, int_value_, true); 530 } 531 } 532 533 // Checks if the current registry entry exists in HKCU\|key_path_|\|name_| 534 // and value is |value_|. If the key does NOT exist in HKCU, checks for 535 // the correct name and value in HKLM. 536 // |look_for_in| specifies roots (HKCU and/or HKLM) in which to look for the 537 // key, unspecified roots are not looked into (i.e. the the key is assumed not 538 // to exist in them). 539 // |look_for_in| must at least specify one root to look into. 540 // If |look_for_in| is LOOK_IN_HKCU_THEN_HKLM, this method mimics Windows' 541 // behavior when searching in HKCR (HKCU takes precedence over HKLM). For 542 // registrations outside of HKCR on versions of Windows prior to Win8, 543 // Chrome's values go in HKLM. This function will make unnecessary (but 544 // harmless) queries into HKCU in that case. 545 bool ExistsInRegistry(uint32 look_for_in) const { 546 DCHECK(look_for_in); 547 548 RegistryStatus status = DOES_NOT_EXIST; 549 if (look_for_in & LOOK_IN_HKCU) 550 status = StatusInRegistryUnderRoot(HKEY_CURRENT_USER); 551 if (status == DOES_NOT_EXIST && (look_for_in & LOOK_IN_HKLM)) 552 status = StatusInRegistryUnderRoot(HKEY_LOCAL_MACHINE); 553 return status == SAME_VALUE; 554 } 555 556 private: 557 // States this RegistryKey can be in compared to the registry. 558 enum RegistryStatus { 559 // |name_| does not exist in the registry 560 DOES_NOT_EXIST, 561 // |name_| exists, but its value != |value_| 562 DIFFERENT_VALUE, 563 // |name_| exists and its value is |value_| 564 SAME_VALUE, 565 }; 566 567 // Create a object that represent default value of a key 568 RegistryEntry(const string16& key_path, const string16& value) 569 : key_path_(key_path), name_(), 570 is_string_(true), value_(value), int_value_(0) { 571 } 572 573 // Create a object that represent a key of type REG_SZ 574 RegistryEntry(const string16& key_path, const string16& name, 575 const string16& value) 576 : key_path_(key_path), name_(name), 577 is_string_(true), value_(value), int_value_(0) { 578 } 579 580 // Create a object that represent a key of integer type 581 RegistryEntry(const string16& key_path, const string16& name, 582 DWORD value) 583 : key_path_(key_path), name_(name), 584 is_string_(false), value_(), int_value_(value) { 585 } 586 587 string16 key_path_; // key path for the registry entry 588 string16 name_; // name of the registry entry 589 bool is_string_; // true if current registry entry is of type REG_SZ 590 string16 value_; // string value (useful if is_string_ = true) 591 DWORD int_value_; // integer value (useful if is_string_ = false) 592 593 // Helper function for ExistsInRegistry(). 594 // Returns the RegistryStatus of the current registry entry in 595 // |root|\|key_path_|\|name_|. 596 RegistryStatus StatusInRegistryUnderRoot(HKEY root) const { 597 RegKey key(root, key_path_.c_str(), KEY_QUERY_VALUE); 598 bool found = false; 599 bool correct_value = false; 600 if (is_string_) { 601 string16 read_value; 602 found = key.ReadValue(name_.c_str(), &read_value) == ERROR_SUCCESS; 603 correct_value = read_value.size() == value_.size() && 604 std::equal(value_.begin(), value_.end(), read_value.begin(), 605 base::CaseInsensitiveCompare<wchar_t>()); 606 } else { 607 DWORD read_value; 608 found = key.ReadValueDW(name_.c_str(), &read_value) == ERROR_SUCCESS; 609 correct_value = read_value == int_value_; 610 } 611 return found ? 612 (correct_value ? SAME_VALUE : DIFFERENT_VALUE) : DOES_NOT_EXIST; 613 } 614 615 DISALLOW_COPY_AND_ASSIGN(RegistryEntry); 616}; // class RegistryEntry 617 618 619// This method converts all the RegistryEntries from the given list to 620// Set/CreateRegWorkItems and runs them using WorkItemList. 621bool AddRegistryEntries(HKEY root, const ScopedVector<RegistryEntry>& entries) { 622 scoped_ptr<WorkItemList> items(WorkItem::CreateWorkItemList()); 623 624 for (ScopedVector<RegistryEntry>::const_iterator itr = entries.begin(); 625 itr != entries.end(); ++itr) 626 (*itr)->AddToWorkItemList(root, items.get()); 627 628 // Apply all the registry changes and if there is a problem, rollback 629 if (!items->Do()) { 630 items->Rollback(); 631 return false; 632 } 633 return true; 634} 635 636// Checks that all |entries| are present on this computer. 637// |look_for_in| is passed to RegistryEntry::ExistsInRegistry(). Documentation 638// for it can be found there. 639bool AreEntriesRegistered(const ScopedVector<RegistryEntry>& entries, 640 uint32 look_for_in) { 641 bool registered = true; 642 for (ScopedVector<RegistryEntry>::const_iterator itr = entries.begin(); 643 registered && itr != entries.end(); ++itr) { 644 // We do not need registered = registered && ... since the loop condition 645 // is set to exit early. 646 registered = (*itr)->ExistsInRegistry(look_for_in); 647 } 648 return registered; 649} 650 651// Checks that all required registry entries for Chrome are already present 652// on this computer. See RegistryEntry::ExistsInRegistry for the behavior of 653// |look_for_in|. 654// Note: between r133333 and r154145 we were registering parts of Chrome in HKCU 655// and parts in HKLM for user-level installs; we now always register everything 656// under a single registry root. Not doing so caused http://crbug.com/144910 for 657// users who first-installed Chrome in that revision range (those users are 658// still impacted by http://crbug.com/144910). This method will keep returning 659// true for affected users (i.e. who have all the registrations, but over both 660// registry roots). 661bool IsChromeRegistered(BrowserDistribution* dist, 662 const string16& chrome_exe, 663 const string16& suffix, 664 uint32 look_for_in) { 665 ScopedVector<RegistryEntry> entries; 666 RegistryEntry::GetProgIdEntries(dist, chrome_exe, suffix, &entries); 667 RegistryEntry::GetShellIntegrationEntries(dist, chrome_exe, suffix, &entries); 668 RegistryEntry::GetAppRegistrationEntries(chrome_exe, suffix, &entries); 669 return AreEntriesRegistered(entries, look_for_in); 670} 671 672// This method checks if Chrome is already registered on the local machine 673// for the requested protocol. It just checks the one value required for this. 674// See RegistryEntry::ExistsInRegistry for the behavior of |look_for_in|. 675bool IsChromeRegisteredForProtocol(BrowserDistribution* dist, 676 const string16& suffix, 677 const string16& protocol, 678 uint32 look_for_in) { 679 ScopedVector<RegistryEntry> entries; 680 RegistryEntry::GetProtocolCapabilityEntries(dist, suffix, protocol, &entries); 681 return AreEntriesRegistered(entries, look_for_in); 682} 683 684// This method registers Chrome on Vista by launching an elevated setup.exe. 685// That will show the user the standard Vista elevation prompt. If the user 686// accepts it the new process will make the necessary changes and return SUCCESS 687// that we capture and return. 688// If protocol is non-empty we will also register Chrome as being capable of 689// handling the protocol. 690bool ElevateAndRegisterChrome(BrowserDistribution* dist, 691 const string16& chrome_exe, 692 const string16& suffix, 693 const string16& protocol) { 694 // Only user-level installs prior to Windows 8 should need to elevate to 695 // register. 696 DCHECK(InstallUtil::IsPerUserInstall(chrome_exe.c_str())); 697 DCHECK_LT(base::win::GetVersion(), base::win::VERSION_WIN8); 698 base::FilePath exe_path = 699 base::FilePath::FromWStringHack(chrome_exe).DirName() 700 .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. 743bool 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. 761bool 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). 781bool 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(ShellUtil::kChromeHTMLProgId); 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. 842bool 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. 871HKEY 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. 879bool 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. 909bool 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". 932string16 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. 952base::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. 973base::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. 1003void 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. 1025bool 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. 1046ShellUtil::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(ShellUtil::kChromeHTMLProgId); 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. 1077ShellUtil::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. 1108ShellUtil::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. 1155ShellUtil::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. 1176bool 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 1201typedef 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. 1208class 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 1228FilterTargetEq::FilterTargetEq(const base::FilePath& desired_target_exe, 1229 bool require_args) 1230 : desired_target_compare_(desired_target_exe), 1231 require_args_(require_args) {} 1232 1233bool 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 1242ShortcutFilterCallback FilterTargetEq::AsShortcutFilterCallback() { 1243 return base::Bind(&FilterTargetEq::Match, base::Unretained(this)); 1244} 1245 1246// Shortcut operations for BatchShortcutAction(). 1247 1248typedef base::Callback<bool(const base::FilePath& /*shortcut_path*/)> 1249 ShortcutOperationCallback; 1250 1251bool 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 1260bool 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 1266bool 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. 1278bool 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. 1316bool 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 && 1321 location != ShellUtil::SHORTCUT_LOCATION_APP_SHORTCUTS) { 1322 NOTREACHED(); 1323 return false; 1324 } 1325 1326 base::FilePath shortcut_folder; 1327 if (!ShellUtil::GetShortcutPath(location, dist, level, &shortcut_folder)) { 1328 LOG(WARNING) << "Cannot find path at location " << location; 1329 return false; 1330 } 1331 if (file_util::IsDirectoryEmpty(shortcut_folder) && 1332 !base::DeleteFile(shortcut_folder, true)) { 1333 LOG(ERROR) << "Cannot remove folder " << shortcut_folder.value(); 1334 return false; 1335 } 1336 return true; 1337} 1338 1339} // namespace 1340 1341const wchar_t* ShellUtil::kRegDefaultIcon = L"\\DefaultIcon"; 1342const wchar_t* ShellUtil::kRegShellPath = L"\\shell"; 1343const wchar_t* ShellUtil::kRegShellOpen = L"\\shell\\open\\command"; 1344const wchar_t* ShellUtil::kRegStartMenuInternet = 1345 L"Software\\Clients\\StartMenuInternet"; 1346const wchar_t* ShellUtil::kRegClasses = L"Software\\Classes"; 1347const wchar_t* ShellUtil::kRegRegisteredApplications = 1348 L"Software\\RegisteredApplications"; 1349const wchar_t* ShellUtil::kRegVistaUrlPrefs = 1350 L"Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\" 1351 L"http\\UserChoice"; 1352const wchar_t* ShellUtil::kAppPathsRegistryKey = 1353 L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths"; 1354const wchar_t* ShellUtil::kAppPathsRegistryPathName = L"Path"; 1355 1356#if defined(GOOGLE_CHROME_BUILD) 1357const wchar_t* ShellUtil::kChromeHTMLProgId = L"ChromeHTML"; 1358const wchar_t* ShellUtil::kChromeHTMLProgIdDesc = L"Chrome HTML Document"; 1359#else 1360// This used to be "ChromiumHTML", but was forced to become "ChromiumHTM" 1361// because of http://crbug.com/153349 as with the '.' and 26 characters suffix 1362// added on user-level installs, the generated progid for Chromium was 39 1363// characters long which, according to MSDN ( 1364// http://msdn.microsoft.com/library/aa911706.aspx), is the maximum length 1365// for a progid. It was however determined through experimentation that the 39 1366// character limit mentioned on MSDN includes the NULL character... 1367const wchar_t* ShellUtil::kChromeHTMLProgId = L"ChromiumHTM"; 1368const wchar_t* ShellUtil::kChromeHTMLProgIdDesc = L"Chromium HTML Document"; 1369#endif 1370 1371const wchar_t* ShellUtil::kDefaultFileAssociations[] = {L".htm", L".html", 1372 L".shtml", L".xht", L".xhtml", NULL}; 1373const wchar_t* ShellUtil::kPotentialFileAssociations[] = {L".htm", L".html", 1374 L".shtml", L".xht", L".xhtml", L".webp", NULL}; 1375const wchar_t* ShellUtil::kBrowserProtocolAssociations[] = {L"ftp", L"http", 1376 L"https", NULL}; 1377const wchar_t* ShellUtil::kPotentialProtocolAssociations[] = {L"ftp", L"http", 1378 L"https", L"irc", L"mailto", L"mms", L"news", L"nntp", L"sms", L"smsto", 1379 L"tel", L"urn", L"webcal", NULL}; 1380const wchar_t* ShellUtil::kRegUrlProtocol = L"URL Protocol"; 1381const wchar_t* ShellUtil::kRegApplication = L"\\Application"; 1382const wchar_t* ShellUtil::kRegAppUserModelId = L"AppUserModelId"; 1383const wchar_t* ShellUtil::kRegApplicationDescription = 1384 L"ApplicationDescription"; 1385const wchar_t* ShellUtil::kRegApplicationName = L"ApplicationName"; 1386const wchar_t* ShellUtil::kRegApplicationIcon = L"ApplicationIcon"; 1387const wchar_t* ShellUtil::kRegApplicationCompany = L"ApplicationCompany"; 1388const wchar_t* ShellUtil::kRegExePath = L"\\.exe"; 1389const wchar_t* ShellUtil::kRegVerbOpen = L"open"; 1390const wchar_t* ShellUtil::kRegVerbOpenNewWindow = L"opennewwindow"; 1391const wchar_t* ShellUtil::kRegVerbRun = L"run"; 1392const wchar_t* ShellUtil::kRegCommand = L"command"; 1393const wchar_t* ShellUtil::kRegDelegateExecute = L"DelegateExecute"; 1394const wchar_t* ShellUtil::kRegOpenWithProgids = L"OpenWithProgids"; 1395 1396bool ShellUtil::QuickIsChromeRegisteredInHKLM(BrowserDistribution* dist, 1397 const string16& chrome_exe, 1398 const string16& suffix) { 1399 return QuickIsChromeRegistered(dist, chrome_exe, suffix, 1400 CONFIRM_SHELL_REGISTRATION_IN_HKLM); 1401} 1402 1403bool ShellUtil::ShortcutLocationIsSupported( 1404 ShellUtil::ShortcutLocation location) { 1405 switch (location) { 1406 case SHORTCUT_LOCATION_DESKTOP: 1407 return true; 1408 case SHORTCUT_LOCATION_QUICK_LAUNCH: 1409 return true; 1410 case SHORTCUT_LOCATION_START_MENU: 1411 return true; 1412 case SHORTCUT_LOCATION_TASKBAR_PINS: 1413 return base::win::GetVersion() >= base::win::VERSION_WIN7; 1414 case SHORTCUT_LOCATION_APP_SHORTCUTS: 1415 return base::win::GetVersion() >= base::win::VERSION_WIN8; 1416 default: 1417 NOTREACHED(); 1418 return false; 1419 } 1420} 1421 1422bool ShellUtil::GetShortcutPath(ShellUtil::ShortcutLocation location, 1423 BrowserDistribution* dist, 1424 ShellChange level, 1425 base::FilePath* path) { 1426 DCHECK(path); 1427 int dir_key = -1; 1428 bool add_folder_for_dist = false; 1429 switch (location) { 1430 case SHORTCUT_LOCATION_DESKTOP: 1431 dir_key = (level == CURRENT_USER) ? base::DIR_USER_DESKTOP : 1432 base::DIR_COMMON_DESKTOP; 1433 break; 1434 case SHORTCUT_LOCATION_QUICK_LAUNCH: 1435 dir_key = (level == CURRENT_USER) ? base::DIR_USER_QUICK_LAUNCH : 1436 base::DIR_DEFAULT_USER_QUICK_LAUNCH; 1437 break; 1438 case SHORTCUT_LOCATION_START_MENU: 1439 dir_key = (level == CURRENT_USER) ? base::DIR_START_MENU : 1440 base::DIR_COMMON_START_MENU; 1441 add_folder_for_dist = true; 1442 break; 1443 case SHORTCUT_LOCATION_TASKBAR_PINS: 1444 dir_key = base::DIR_TASKBAR_PINS; 1445 break; 1446 case SHORTCUT_LOCATION_APP_SHORTCUTS: 1447 // TODO(huangs): Move GetAppShortcutsFolder() logic into base_paths_win. 1448 return GetAppShortcutsFolder(dist, level, path); 1449 1450 default: 1451 NOTREACHED(); 1452 return false; 1453 } 1454 1455 if (!PathService::Get(dir_key, path) || path->empty()) { 1456 NOTREACHED() << dir_key; 1457 return false; 1458 } 1459 1460 if (add_folder_for_dist) { 1461 *path = path->Append(dist->GetStartMenuShortcutSubfolder( 1462 BrowserDistribution::SUBFOLDER_CHROME)); 1463 } 1464 1465 return true; 1466} 1467 1468bool ShellUtil::CreateOrUpdateShortcut( 1469 ShellUtil::ShortcutLocation location, 1470 BrowserDistribution* dist, 1471 const ShellUtil::ShortcutProperties& properties, 1472 ShellUtil::ShortcutOperation operation) { 1473 // Explicitly whitelist locations to which this is applicable. 1474 if (location != SHORTCUT_LOCATION_DESKTOP && 1475 location != SHORTCUT_LOCATION_QUICK_LAUNCH && 1476 location != SHORTCUT_LOCATION_START_MENU) { 1477 NOTREACHED(); 1478 return false; 1479 } 1480 1481 DCHECK(dist); 1482 // |pin_to_taskbar| is only acknowledged when first creating the shortcut. 1483 DCHECK(!properties.pin_to_taskbar || 1484 operation == SHELL_SHORTCUT_CREATE_ALWAYS || 1485 operation == SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL); 1486 1487 base::FilePath user_shortcut_path; 1488 base::FilePath system_shortcut_path; 1489 if (!GetShortcutPath(location, dist, SYSTEM_LEVEL, &system_shortcut_path)) { 1490 NOTREACHED(); 1491 return false; 1492 } 1493 1494 string16 shortcut_name(ExtractShortcutNameFromProperties(dist, properties)); 1495 system_shortcut_path = system_shortcut_path.Append(shortcut_name); 1496 1497 base::FilePath* chosen_path; 1498 bool should_install_shortcut = true; 1499 if (properties.level == SYSTEM_LEVEL) { 1500 // Install the system-level shortcut if requested. 1501 chosen_path = &system_shortcut_path; 1502 } else if (operation != SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL || 1503 !base::PathExists(system_shortcut_path)) { 1504 // Otherwise install the user-level shortcut, unless the system-level 1505 // variant of this shortcut is present on the machine and |operation| states 1506 // not to create a user-level shortcut in that case. 1507 if (!GetShortcutPath(location, dist, CURRENT_USER, &user_shortcut_path)) { 1508 NOTREACHED(); 1509 return false; 1510 } 1511 user_shortcut_path = user_shortcut_path.Append(shortcut_name); 1512 chosen_path = &user_shortcut_path; 1513 } else { 1514 // Do not install any shortcut if we are told to install a user-level 1515 // shortcut, but the system-level variant of that shortcut is present. 1516 // Other actions (e.g., pinning) can still happen with respect to the 1517 // existing system-level shortcut however. 1518 chosen_path = &system_shortcut_path; 1519 should_install_shortcut = false; 1520 } 1521 1522 if (chosen_path == NULL || chosen_path->empty()) { 1523 NOTREACHED(); 1524 return false; 1525 } 1526 1527 base::win::ShortcutOperation shortcut_operation = 1528 TranslateShortcutOperation(operation); 1529 bool ret = true; 1530 if (should_install_shortcut) { 1531 // Make sure the parent directories exist when creating the shortcut. 1532 if (shortcut_operation == base::win::SHORTCUT_CREATE_ALWAYS && 1533 !file_util::CreateDirectory(chosen_path->DirName())) { 1534 NOTREACHED(); 1535 return false; 1536 } 1537 1538 base::win::ShortcutProperties shortcut_properties( 1539 TranslateShortcutProperties(properties)); 1540 ret = base::win::CreateOrUpdateShortcutLink( 1541 *chosen_path, shortcut_properties, shortcut_operation); 1542 } 1543 1544 if (ret && shortcut_operation == base::win::SHORTCUT_CREATE_ALWAYS && 1545 properties.pin_to_taskbar && 1546 base::win::GetVersion() >= base::win::VERSION_WIN7) { 1547 ret = base::win::TaskbarPinShortcutLink(chosen_path->value().c_str()); 1548 if (!ret) { 1549 LOG(ERROR) << "Failed to pin " << chosen_path->value(); 1550 } 1551 } 1552 1553 return ret; 1554} 1555 1556string16 ShellUtil::FormatIconLocation(const string16& icon_path, 1557 int icon_index) { 1558 string16 icon_string(icon_path); 1559 icon_string.append(L","); 1560 icon_string.append(base::IntToString16(icon_index)); 1561 return icon_string; 1562} 1563 1564string16 ShellUtil::GetChromeShellOpenCmd(const string16& chrome_exe) { 1565 return L"\"" + chrome_exe + L"\" -- \"%1\""; 1566} 1567 1568string16 ShellUtil::GetChromeDelegateCommand(const string16& chrome_exe) { 1569 return L"\"" + chrome_exe + L"\" -- %*"; 1570} 1571 1572void ShellUtil::GetRegisteredBrowsers( 1573 BrowserDistribution* dist, 1574 std::map<string16, string16>* browsers) { 1575 DCHECK(dist); 1576 DCHECK(browsers); 1577 1578 const string16 base_key(ShellUtil::kRegStartMenuInternet); 1579 string16 client_path; 1580 RegKey key; 1581 string16 name; 1582 string16 command; 1583 1584 // HKCU has precedence over HKLM for these registrations: http://goo.gl/xjczJ. 1585 // Look in HKCU second to override any identical values found in HKLM. 1586 const HKEY roots[] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER }; 1587 for (int i = 0; i < arraysize(roots); ++i) { 1588 const HKEY root = roots[i]; 1589 for (base::win::RegistryKeyIterator iter(root, base_key.c_str()); 1590 iter.Valid(); ++iter) { 1591 client_path.assign(base_key).append(1, L'\\').append(iter.Name()); 1592 // Read the browser's name (localized according to install language). 1593 if (key.Open(root, client_path.c_str(), 1594 KEY_QUERY_VALUE) != ERROR_SUCCESS || 1595 key.ReadValue(NULL, &name) != ERROR_SUCCESS || 1596 name.empty() || 1597 name.find(dist->GetBaseAppName()) != string16::npos) { 1598 continue; 1599 } 1600 // Read the browser's reinstall command. 1601 if (key.Open(root, (client_path + L"\\InstallInfo").c_str(), 1602 KEY_QUERY_VALUE) == ERROR_SUCCESS && 1603 key.ReadValue(kReinstallCommand, &command) == ERROR_SUCCESS && 1604 !command.empty()) { 1605 (*browsers)[name] = command; 1606 } 1607 } 1608 } 1609} 1610 1611string16 ShellUtil::GetCurrentInstallationSuffix(BrowserDistribution* dist, 1612 const string16& chrome_exe) { 1613 // This method is somewhat the opposite of GetInstallationSpecificSuffix(). 1614 // In this case we are not trying to determine the current suffix for the 1615 // upcoming installation (i.e. not trying to stick to a currently bad 1616 // registration style if one is present). 1617 // Here we want to determine which suffix we should use at run-time. 1618 // In order of preference, we prefer (for user-level installs): 1619 // 1) Base 32 encoding of the md5 hash of the user's sid (new-style). 1620 // 2) Username (old-style). 1621 // 3) Unsuffixed (even worse). 1622 string16 tested_suffix; 1623 if (InstallUtil::IsPerUserInstall(chrome_exe.c_str()) && 1624 (!GetUserSpecificRegistrySuffix(&tested_suffix) || 1625 !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix, 1626 CONFIRM_PROGID_REGISTRATION)) && 1627 (!GetOldUserSpecificRegistrySuffix(&tested_suffix) || 1628 !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix, 1629 CONFIRM_PROGID_REGISTRATION)) && 1630 !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix.erase(), 1631 CONFIRM_PROGID_REGISTRATION)) { 1632 // If Chrome is not registered under any of the possible suffixes (e.g. 1633 // tests, Canary, etc.): use the new-style suffix at run-time. 1634 if (!GetUserSpecificRegistrySuffix(&tested_suffix)) 1635 NOTREACHED(); 1636 } 1637 return tested_suffix; 1638} 1639 1640string16 ShellUtil::GetApplicationName(BrowserDistribution* dist, 1641 const string16& chrome_exe) { 1642 string16 app_name = dist->GetBaseAppName(); 1643 app_name += GetCurrentInstallationSuffix(dist, chrome_exe); 1644 return app_name; 1645} 1646 1647string16 ShellUtil::GetBrowserModelId(BrowserDistribution* dist, 1648 bool is_per_user_install) { 1649 string16 app_id(dist->GetBaseAppId()); 1650 string16 suffix; 1651 1652 // TODO(robertshield): Temporary hack to make the kRegisterChromeBrowserSuffix 1653 // apply to all registry values computed down in these murky depths. 1654 CommandLine& command_line = *CommandLine::ForCurrentProcess(); 1655 if (command_line.HasSwitch( 1656 installer::switches::kRegisterChromeBrowserSuffix)) { 1657 suffix = command_line.GetSwitchValueNative( 1658 installer::switches::kRegisterChromeBrowserSuffix); 1659 } else if (is_per_user_install && !GetUserSpecificRegistrySuffix(&suffix)) { 1660 NOTREACHED(); 1661 } 1662 // There is only one component (i.e. the suffixed appid) in this case, but it 1663 // is still necessary to go through the appid constructor to make sure the 1664 // returned appid is truncated if necessary. 1665 std::vector<string16> components(1, app_id.append(suffix)); 1666 return BuildAppModelId(components); 1667} 1668 1669string16 ShellUtil::BuildAppModelId( 1670 const std::vector<string16>& components) { 1671 DCHECK_GT(components.size(), 0U); 1672 1673 // Find the maximum numbers of characters allowed in each component 1674 // (accounting for the dots added between each component). 1675 const size_t available_chars = 1676 installer::kMaxAppModelIdLength - (components.size() - 1); 1677 const size_t max_component_length = available_chars / components.size(); 1678 1679 // |max_component_length| should be at least 2; otherwise the truncation logic 1680 // below breaks. 1681 if (max_component_length < 2U) { 1682 NOTREACHED(); 1683 return (*components.begin()).substr(0, installer::kMaxAppModelIdLength); 1684 } 1685 1686 string16 app_id; 1687 app_id.reserve(installer::kMaxAppModelIdLength); 1688 for (std::vector<string16>::const_iterator it = components.begin(); 1689 it != components.end(); ++it) { 1690 if (it != components.begin()) 1691 app_id.push_back(L'.'); 1692 1693 const string16& component = *it; 1694 DCHECK(!component.empty()); 1695 if (component.length() > max_component_length) { 1696 // Append a shortened version of this component. Cut in the middle to try 1697 // to avoid losing the unique parts of this component (which are usually 1698 // at the beginning or end for things like usernames and paths). 1699 app_id.append(component.c_str(), 0, max_component_length / 2); 1700 app_id.append(component.c_str(), 1701 component.length() - ((max_component_length + 1) / 2), 1702 string16::npos); 1703 } else { 1704 app_id.append(component); 1705 } 1706 } 1707 // No spaces are allowed in the AppUserModelId according to MSDN. 1708 ReplaceChars(app_id, L" ", L"_", &app_id); 1709 return app_id; 1710} 1711 1712ShellUtil::DefaultState ShellUtil::GetChromeDefaultState() { 1713 // When we check for default browser we don't necessarily want to count file 1714 // type handlers and icons as having changed the default browser status, 1715 // since the user may have changed their shell settings to cause HTML files 1716 // to open with a text editor for example. We also don't want to aggressively 1717 // claim FTP, since the user may have a separate FTP client. It is an open 1718 // question as to how to "heal" these settings. Perhaps the user should just 1719 // re-run the installer or run with the --set-default-browser command line 1720 // flag. There is doubtless some other key we can hook into to cause "Repair" 1721 // to show up in Add/Remove programs for us. 1722 static const wchar_t* const kChromeProtocols[] = { L"http", L"https" }; 1723 return ProbeProtocolHandlers(kChromeProtocols, arraysize(kChromeProtocols)); 1724} 1725 1726ShellUtil::DefaultState ShellUtil::GetChromeDefaultProtocolClientState( 1727 const string16& protocol) { 1728 if (protocol.empty()) 1729 return UNKNOWN_DEFAULT; 1730 1731 const wchar_t* const protocols[] = { protocol.c_str() }; 1732 return ProbeProtocolHandlers(protocols, arraysize(protocols)); 1733} 1734 1735// static 1736bool ShellUtil::CanMakeChromeDefaultUnattended() { 1737 return base::win::GetVersion() < base::win::VERSION_WIN8; 1738} 1739 1740bool ShellUtil::MakeChromeDefault(BrowserDistribution* dist, 1741 int shell_change, 1742 const string16& chrome_exe, 1743 bool elevate_if_not_admin) { 1744 DCHECK(!(shell_change & ShellUtil::SYSTEM_LEVEL) || IsUserAnAdmin()); 1745 1746 if (!dist->CanSetAsDefault()) 1747 return false; 1748 1749 // Windows 8 does not permit making a browser default just like that. 1750 // This process needs to be routed through the system's UI. Use 1751 // ShowMakeChromeDefaultSystemUI instead (below). 1752 if (!CanMakeChromeDefaultUnattended()) { 1753 return false; 1754 } 1755 1756 if (!ShellUtil::RegisterChromeBrowser( 1757 dist, chrome_exe, string16(), elevate_if_not_admin)) { 1758 return false; 1759 } 1760 1761 bool ret = true; 1762 // First use the new "recommended" way on Vista to make Chrome default 1763 // browser. 1764 string16 app_name = GetApplicationName(dist, chrome_exe); 1765 1766 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { 1767 // On Windows Vista and Win7 we still can set ourselves via the 1768 // the IApplicationAssociationRegistration interface. 1769 VLOG(1) << "Registering Chrome as default browser on Vista."; 1770 base::win::ScopedComPtr<IApplicationAssociationRegistration> pAAR; 1771 HRESULT hr = pAAR.CreateInstance(CLSID_ApplicationAssociationRegistration, 1772 NULL, CLSCTX_INPROC); 1773 if (SUCCEEDED(hr)) { 1774 for (int i = 0; ShellUtil::kBrowserProtocolAssociations[i] != NULL; i++) { 1775 hr = pAAR->SetAppAsDefault(app_name.c_str(), 1776 ShellUtil::kBrowserProtocolAssociations[i], AT_URLPROTOCOL); 1777 if (!SUCCEEDED(hr)) { 1778 ret = false; 1779 LOG(ERROR) << "Failed to register as default for protocol " 1780 << ShellUtil::kBrowserProtocolAssociations[i] 1781 << " (" << hr << ")"; 1782 } 1783 } 1784 1785 for (int i = 0; ShellUtil::kDefaultFileAssociations[i] != NULL; i++) { 1786 hr = pAAR->SetAppAsDefault(app_name.c_str(), 1787 ShellUtil::kDefaultFileAssociations[i], AT_FILEEXTENSION); 1788 if (!SUCCEEDED(hr)) { 1789 ret = false; 1790 LOG(ERROR) << "Failed to register as default for file extension " 1791 << ShellUtil::kDefaultFileAssociations[i] 1792 << " (" << hr << ")"; 1793 } 1794 } 1795 } 1796 } 1797 1798 if (!RegisterChromeAsDefaultXPStyle(dist, shell_change, chrome_exe)) 1799 ret = false; 1800 1801 // Send Windows notification event so that it can update icons for 1802 // file associations. 1803 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL); 1804 return ret; 1805} 1806 1807bool ShellUtil::ShowMakeChromeDefaultSystemUI(BrowserDistribution* dist, 1808 const string16& chrome_exe) { 1809 DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8); 1810 if (!dist->CanSetAsDefault()) 1811 return false; 1812 1813 if (!RegisterChromeBrowser(dist, chrome_exe, string16(), true)) 1814 return false; 1815 1816 bool succeeded = true; 1817 bool is_default = (GetChromeDefaultState() == IS_DEFAULT); 1818 if (!is_default) { 1819 // On Windows 8, you can't set yourself as the default handler 1820 // programatically. In other words IApplicationAssociationRegistration 1821 // has been rendered useless. What you can do is to launch 1822 // "Set Program Associations" section of the "Default Programs" 1823 // control panel, which is a mess, or pop the concise "How you want to open 1824 // webpages?" dialog. We choose the latter. 1825 succeeded = LaunchSelectDefaultProtocolHandlerDialog(L"http"); 1826 is_default = (succeeded && GetChromeDefaultState() == IS_DEFAULT); 1827 } 1828 if (succeeded && is_default) 1829 RegisterChromeAsDefaultXPStyle(dist, CURRENT_USER, chrome_exe); 1830 return succeeded; 1831} 1832 1833bool ShellUtil::MakeChromeDefaultProtocolClient(BrowserDistribution* dist, 1834 const string16& chrome_exe, 1835 const string16& protocol) { 1836 if (!dist->CanSetAsDefault()) 1837 return false; 1838 1839 if (!RegisterChromeForProtocol(dist, chrome_exe, string16(), protocol, true)) 1840 return false; 1841 1842 // Windows 8 does not permit making a browser default just like that. 1843 // This process needs to be routed through the system's UI. Use 1844 // ShowMakeChromeDefaultProocolClientSystemUI instead (below). 1845 if (!CanMakeChromeDefaultUnattended()) 1846 return false; 1847 1848 bool ret = true; 1849 // First use the new "recommended" way on Vista to make Chrome default 1850 // protocol handler. 1851 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { 1852 VLOG(1) << "Registering Chrome as default handler for " << protocol 1853 << " on Vista."; 1854 base::win::ScopedComPtr<IApplicationAssociationRegistration> pAAR; 1855 HRESULT hr = pAAR.CreateInstance(CLSID_ApplicationAssociationRegistration, 1856 NULL, CLSCTX_INPROC); 1857 if (SUCCEEDED(hr)) { 1858 string16 app_name = GetApplicationName(dist, chrome_exe); 1859 hr = pAAR->SetAppAsDefault(app_name.c_str(), protocol.c_str(), 1860 AT_URLPROTOCOL); 1861 } 1862 if (!SUCCEEDED(hr)) { 1863 ret = false; 1864 LOG(ERROR) << "Could not make Chrome default protocol client (Vista):" 1865 << " HRESULT=" << hr << "."; 1866 } 1867 } 1868 1869 // Now use the old way to associate Chrome with the desired protocol. This 1870 // should not be required on Vista+, but since some applications still read 1871 // Software\Classes\<protocol> key directly, do this on Vista+ also. 1872 if (!RegisterChromeAsDefaultProtocolClientXPStyle(dist, chrome_exe, protocol)) 1873 ret = false; 1874 1875 return ret; 1876} 1877 1878bool ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI( 1879 BrowserDistribution* dist, 1880 const string16& chrome_exe, 1881 const string16& protocol) { 1882 DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8); 1883 if (!dist->CanSetAsDefault()) 1884 return false; 1885 1886 if (!RegisterChromeForProtocol(dist, chrome_exe, string16(), protocol, true)) 1887 return false; 1888 1889 bool succeeded = true; 1890 bool is_default = ( 1891 GetChromeDefaultProtocolClientState(protocol) == IS_DEFAULT); 1892 if (!is_default) { 1893 // On Windows 8, you can't set yourself as the default handler 1894 // programatically. In other words IApplicationAssociationRegistration 1895 // has been rendered useless. What you can do is to launch 1896 // "Set Program Associations" section of the "Default Programs" 1897 // control panel, which is a mess, or pop the concise "How you want to open 1898 // links of this type (protocol)?" dialog. We choose the latter. 1899 succeeded = LaunchSelectDefaultProtocolHandlerDialog(protocol.c_str()); 1900 is_default = (succeeded && 1901 GetChromeDefaultProtocolClientState(protocol) == IS_DEFAULT); 1902 } 1903 if (succeeded && is_default) 1904 RegisterChromeAsDefaultProtocolClientXPStyle(dist, chrome_exe, protocol); 1905 return succeeded; 1906} 1907 1908bool ShellUtil::RegisterChromeBrowser(BrowserDistribution* dist, 1909 const string16& chrome_exe, 1910 const string16& unique_suffix, 1911 bool elevate_if_not_admin) { 1912 if (!dist->CanSetAsDefault()) 1913 return false; 1914 1915 CommandLine& command_line = *CommandLine::ForCurrentProcess(); 1916 1917 string16 suffix; 1918 if (!unique_suffix.empty()) { 1919 suffix = unique_suffix; 1920 } else if (command_line.HasSwitch( 1921 installer::switches::kRegisterChromeBrowserSuffix)) { 1922 suffix = command_line.GetSwitchValueNative( 1923 installer::switches::kRegisterChromeBrowserSuffix); 1924 } else if (!GetInstallationSpecificSuffix(dist, chrome_exe, &suffix)) { 1925 return false; 1926 } 1927 1928 RemoveRunVerbOnWindows8(dist, chrome_exe); 1929 1930 bool user_level = InstallUtil::IsPerUserInstall(chrome_exe.c_str()); 1931 HKEY root = DetermineRegistrationRoot(user_level); 1932 1933 // Look only in HKLM for system-level installs (otherwise, if a user-level 1934 // install is also present, it will lead IsChromeRegistered() to think this 1935 // system-level install isn't registered properly as it is shadowed by the 1936 // user-level install's registrations). 1937 uint32 look_for_in = user_level ? 1938 RegistryEntry::LOOK_IN_HKCU_THEN_HKLM : RegistryEntry::LOOK_IN_HKLM; 1939 1940 // Check if chrome is already registered with this suffix. 1941 if (IsChromeRegistered(dist, chrome_exe, suffix, look_for_in)) 1942 return true; 1943 1944 bool result = true; 1945 if (root == HKEY_CURRENT_USER || IsUserAnAdmin()) { 1946 // Do the full registration if we can do it at user-level or if the user is 1947 // an admin. 1948 ScopedVector<RegistryEntry> progid_and_appreg_entries; 1949 ScopedVector<RegistryEntry> shell_entries; 1950 RegistryEntry::GetProgIdEntries(dist, chrome_exe, suffix, 1951 &progid_and_appreg_entries); 1952 RegistryEntry::GetAppRegistrationEntries(chrome_exe, suffix, 1953 &progid_and_appreg_entries); 1954 RegistryEntry::GetShellIntegrationEntries( 1955 dist, chrome_exe, suffix, &shell_entries); 1956 result = (AddRegistryEntries(root, progid_and_appreg_entries) && 1957 AddRegistryEntries(root, shell_entries)); 1958 } else if (elevate_if_not_admin && 1959 base::win::GetVersion() >= base::win::VERSION_VISTA && 1960 ElevateAndRegisterChrome(dist, chrome_exe, suffix, L"")) { 1961 // If the user is not an admin and OS is between Vista and Windows 7 1962 // inclusively, try to elevate and register. This is only intended for 1963 // user-level installs as system-level installs should always be run with 1964 // admin rights. 1965 result = true; 1966 } else { 1967 // If we got to this point then all we can do is create ProgId and basic app 1968 // registrations under HKCU. 1969 ScopedVector<RegistryEntry> entries; 1970 RegistryEntry::GetProgIdEntries(dist, chrome_exe, string16(), &entries); 1971 // Prefer to use |suffix|; unless Chrome's ProgIds are already registered 1972 // with no suffix (as per the old registration style): in which case some 1973 // other registry entries could refer to them and since we were not able to 1974 // set our HKLM entries above, we are better off not altering these here. 1975 if (!AreEntriesRegistered(entries, RegistryEntry::LOOK_IN_HKCU)) { 1976 if (!suffix.empty()) { 1977 entries.clear(); 1978 RegistryEntry::GetProgIdEntries(dist, chrome_exe, suffix, &entries); 1979 RegistryEntry::GetAppRegistrationEntries(chrome_exe, suffix, &entries); 1980 } 1981 result = AddRegistryEntries(HKEY_CURRENT_USER, entries); 1982 } else { 1983 // The ProgId is registered unsuffixed in HKCU, also register the app with 1984 // Windows in HKCU (this was not done in the old registration style and 1985 // thus needs to be done after the above check for the unsuffixed 1986 // registration). 1987 entries.clear(); 1988 RegistryEntry::GetAppRegistrationEntries(chrome_exe, string16(), 1989 &entries); 1990 result = AddRegistryEntries(HKEY_CURRENT_USER, entries); 1991 } 1992 } 1993 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL); 1994 return result; 1995} 1996 1997bool ShellUtil::RegisterChromeForProtocol(BrowserDistribution* dist, 1998 const string16& chrome_exe, 1999 const string16& unique_suffix, 2000 const string16& protocol, 2001 bool elevate_if_not_admin) { 2002 if (!dist->CanSetAsDefault()) 2003 return false; 2004 2005 string16 suffix; 2006 if (!unique_suffix.empty()) { 2007 suffix = unique_suffix; 2008 } else if (!GetInstallationSpecificSuffix(dist, chrome_exe, &suffix)) { 2009 return false; 2010 } 2011 2012 bool user_level = InstallUtil::IsPerUserInstall(chrome_exe.c_str()); 2013 HKEY root = DetermineRegistrationRoot(user_level); 2014 2015 // Look only in HKLM for system-level installs (otherwise, if a user-level 2016 // install is also present, it could lead IsChromeRegisteredForProtocol() to 2017 // think this system-level install isn't registered properly as it may be 2018 // shadowed by the user-level install's registrations). 2019 uint32 look_for_in = user_level ? 2020 RegistryEntry::LOOK_IN_HKCU_THEN_HKLM : RegistryEntry::LOOK_IN_HKLM; 2021 2022 // Check if chrome is already registered with this suffix. 2023 if (IsChromeRegisteredForProtocol(dist, suffix, protocol, look_for_in)) 2024 return true; 2025 2026 if (root == HKEY_CURRENT_USER || IsUserAnAdmin()) { 2027 // We can do this operation directly. 2028 // First, make sure Chrome is fully registered on this machine. 2029 if (!RegisterChromeBrowser(dist, chrome_exe, suffix, false)) 2030 return false; 2031 2032 // Write in the capabillity for the protocol. 2033 ScopedVector<RegistryEntry> entries; 2034 RegistryEntry::GetProtocolCapabilityEntries(dist, suffix, protocol, 2035 &entries); 2036 return AddRegistryEntries(root, entries); 2037 } else if (elevate_if_not_admin && 2038 base::win::GetVersion() >= base::win::VERSION_VISTA) { 2039 // Elevate to do the whole job 2040 return ElevateAndRegisterChrome(dist, chrome_exe, suffix, protocol); 2041 } else { 2042 // Admin rights are required to register capabilities before Windows 8. 2043 return false; 2044 } 2045} 2046 2047// static 2048bool ShellUtil::RemoveShortcuts(ShellUtil::ShortcutLocation location, 2049 BrowserDistribution* dist, 2050 ShellChange level, 2051 const base::FilePath& target_exe) { 2052 if (!ShellUtil::ShortcutLocationIsSupported(location)) 2053 return true; // Vacuous success. 2054 2055 FilterTargetEq shortcut_filter(target_exe, false); 2056 // Main operation to apply to each shortcut in the directory specified. 2057 ShortcutOperationCallback shortcut_operation( 2058 location == SHORTCUT_LOCATION_TASKBAR_PINS ? 2059 base::Bind(&ShortcutOpUnpin) : base::Bind(&ShortcutOpDelete)); 2060 bool success = BatchShortcutAction(shortcut_filter.AsShortcutFilterCallback(), 2061 shortcut_operation, location, dist, level); 2062 // Remove chrome-specific shortcut folders if they are now empty. 2063 if (success && 2064 (location == SHORTCUT_LOCATION_START_MENU || 2065 location == SHORTCUT_LOCATION_APP_SHORTCUTS)) { 2066 success = RemoveShortcutFolderIfEmpty(location, dist, level); 2067 } 2068 return success; 2069} 2070 2071// static 2072bool ShellUtil::UpdateShortcuts( 2073 ShellUtil::ShortcutLocation location, 2074 BrowserDistribution* dist, 2075 ShellChange level, 2076 const base::FilePath& target_exe, 2077 bool require_args, 2078 const ShellUtil::ShortcutProperties& properties) { 2079 if (!ShellUtil::ShortcutLocationIsSupported(location)) 2080 return true; // Vacuous success. 2081 2082 FilterTargetEq shortcut_filter(target_exe, require_args); 2083 ShortcutOperationCallback shortcut_operation( 2084 base::Bind(&ShortcutOpUpdate, TranslateShortcutProperties(properties))); 2085 return BatchShortcutAction(shortcut_filter.AsShortcutFilterCallback(), 2086 shortcut_operation, location, dist, level); 2087} 2088 2089bool ShellUtil::GetUserSpecificRegistrySuffix(string16* suffix) { 2090 // Use a thread-safe cache for the user's suffix. 2091 static base::LazyInstance<UserSpecificRegistrySuffix>::Leaky suffix_instance = 2092 LAZY_INSTANCE_INITIALIZER; 2093 return suffix_instance.Get().GetSuffix(suffix); 2094} 2095 2096bool ShellUtil::GetOldUserSpecificRegistrySuffix(string16* suffix) { 2097 wchar_t user_name[256]; 2098 DWORD size = arraysize(user_name); 2099 if (::GetUserName(user_name, &size) == 0 || size < 1) { 2100 NOTREACHED(); 2101 return false; 2102 } 2103 suffix->reserve(size); 2104 suffix->assign(1, L'.'); 2105 suffix->append(user_name, size - 1); 2106 return true; 2107} 2108 2109string16 ShellUtil::ByteArrayToBase32(const uint8* bytes, size_t size) { 2110 static const char kEncoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; 2111 2112 // Eliminate special cases first. 2113 if (size == 0) { 2114 return string16(); 2115 } else if (size == 1) { 2116 string16 ret; 2117 ret.push_back(kEncoding[(bytes[0] & 0xf8) >> 3]); 2118 ret.push_back(kEncoding[(bytes[0] & 0x07) << 2]); 2119 return ret; 2120 } else if (size >= std::numeric_limits<size_t>::max() / 8) { 2121 // If |size| is too big, the calculation of |encoded_length| below will 2122 // overflow. 2123 NOTREACHED(); 2124 return string16(); 2125 } 2126 2127 // Overestimate the number of bits in the string by 4 so that dividing by 5 2128 // is the equivalent of rounding up the actual number of bits divided by 5. 2129 const size_t encoded_length = (size * 8 + 4) / 5; 2130 2131 string16 ret; 2132 ret.reserve(encoded_length); 2133 2134 // A bit stream which will be read from the left and appended to from the 2135 // right as it's emptied. 2136 uint16 bit_stream = (bytes[0] << 8) + bytes[1]; 2137 size_t next_byte_index = 2; 2138 int free_bits = 0; 2139 while (free_bits < 16) { 2140 // Extract the 5 leftmost bits in the stream 2141 ret.push_back(kEncoding[(bit_stream & 0xf800) >> 11]); 2142 bit_stream <<= 5; 2143 free_bits += 5; 2144 2145 // If there is enough room in the bit stream, inject another byte (if there 2146 // are any left...). 2147 if (free_bits >= 8 && next_byte_index < size) { 2148 free_bits -= 8; 2149 bit_stream += bytes[next_byte_index++] << free_bits; 2150 } 2151 } 2152 2153 DCHECK_EQ(ret.length(), encoded_length); 2154 return ret; 2155} 2156