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