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