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