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