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