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