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