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