Switch to EnhancedBookmarkModel from meta_accessor
[chromium-blink-merge.git] / chrome / installer / util / shell_util.cc
blobfd1a6c35d1f328603487641476e10f2d7f2fecae
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/files/file_enumerator.h"
21 #include "base/files/file_path.h"
22 #include "base/files/file_util.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/stringprintf.h"
34 #include "base/strings/utf_string_conversions.h"
35 #include "base/synchronization/cancellation_flag.h"
36 #include "base/values.h"
37 #include "base/win/registry.h"
38 #include "base/win/scoped_co_mem.h"
39 #include "base/win/scoped_comptr.h"
40 #include "base/win/shortcut.h"
41 #include "base/win/win_util.h"
42 #include "base/win/windows_version.h"
43 #include "chrome/common/chrome_constants.h"
44 #include "chrome/common/chrome_switches.h"
45 #include "chrome/installer/util/browser_distribution.h"
46 #include "chrome/installer/util/install_util.h"
47 #include "chrome/installer/util/l10n_string_util.h"
48 #include "chrome/installer/util/master_preferences.h"
49 #include "chrome/installer/util/master_preferences_constants.h"
50 #include "chrome/installer/util/util_constants.h"
51 #include "chrome/installer/util/work_item.h"
53 #include "installer_util_strings.h" // NOLINT
55 using base::win::RegKey;
57 namespace {
59 // An enum used to tell QuickIsChromeRegistered() which level of registration
60 // the caller wants to confirm.
61 enum RegistrationConfirmationLevel {
62 // Only look for Chrome's ProgIds.
63 // This is sufficient when we are trying to determine the suffix of the
64 // currently running Chrome as shell integration registrations might not be
65 // present.
66 CONFIRM_PROGID_REGISTRATION = 0,
67 // Confirm that Chrome is fully integrated with Windows (i.e. registered with
68 // Defaut Programs). These registrations can be in HKCU as of Windows 8.
69 // Note: Shell registration implies ProgId registration.
70 CONFIRM_SHELL_REGISTRATION,
71 // Same as CONFIRM_SHELL_REGISTRATION, but only look in HKLM (used when
72 // uninstalling to know whether elevation is required to clean up the
73 // registry).
74 CONFIRM_SHELL_REGISTRATION_IN_HKLM,
77 const wchar_t kReinstallCommand[] = L"ReinstallCommand";
79 // Returns true if Chrome Metro is supported on this OS (Win 8 8370 or greater).
80 // TODO(gab): Change this to a simple check for Win 8 once old Win8 builds
81 // become irrelevant.
82 bool IsChromeMetroSupported() {
83 OSVERSIONINFOEX min_version_info = {};
84 min_version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
85 min_version_info.dwMajorVersion = 6;
86 min_version_info.dwMinorVersion = 2;
87 min_version_info.dwBuildNumber = 8370;
88 min_version_info.wServicePackMajor = 0;
89 min_version_info.wServicePackMinor = 0;
91 DWORDLONG condition_mask = 0;
92 VER_SET_CONDITION(condition_mask, VER_MAJORVERSION, VER_GREATER_EQUAL);
93 VER_SET_CONDITION(condition_mask, VER_MINORVERSION, VER_GREATER_EQUAL);
94 VER_SET_CONDITION(condition_mask, VER_BUILDNUMBER, VER_GREATER_EQUAL);
95 VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
96 VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL);
98 DWORD type_mask = VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER |
99 VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR;
101 return VerifyVersionInfo(&min_version_info, type_mask, condition_mask) != 0;
104 // Returns the current (or installed) browser's ProgId (e.g.
105 // "ChromeHTML|suffix|").
106 // |suffix| can be the empty string.
107 base::string16 GetBrowserProgId(const base::string16& suffix) {
108 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
109 base::string16 chrome_html(dist->GetBrowserProgIdPrefix());
110 chrome_html.append(suffix);
112 // ProgIds cannot be longer than 39 characters.
113 // Ref: http://msdn.microsoft.com/en-us/library/aa911706.aspx.
114 // Make all new registrations comply with this requirement (existing
115 // registrations must be preserved).
116 base::string16 new_style_suffix;
117 if (ShellUtil::GetUserSpecificRegistrySuffix(&new_style_suffix) &&
118 suffix == new_style_suffix && chrome_html.length() > 39) {
119 NOTREACHED();
120 chrome_html.erase(39);
122 return chrome_html;
125 // This class is used to initialize and cache a base 32 encoding of the md5 hash
126 // of this user's sid preceded by a dot.
127 // This is guaranteed to be unique on the machine and 27 characters long
128 // (including the '.').
129 // This is then meant to be used as a suffix on all registrations that may
130 // conflict with another user-level Chrome install.
131 class UserSpecificRegistrySuffix {
132 public:
133 // All the initialization is done in the constructor to be able to build the
134 // suffix in a thread-safe manner when used in conjunction with a
135 // LazyInstance.
136 UserSpecificRegistrySuffix();
138 // Sets |suffix| to the pre-computed suffix cached in this object.
139 // Returns true unless the initialization originally failed.
140 bool GetSuffix(base::string16* suffix);
142 private:
143 base::string16 suffix_;
145 DISALLOW_COPY_AND_ASSIGN(UserSpecificRegistrySuffix);
146 }; // class UserSpecificRegistrySuffix
148 UserSpecificRegistrySuffix::UserSpecificRegistrySuffix() {
149 base::string16 user_sid;
150 if (!base::win::GetUserSidString(&user_sid)) {
151 NOTREACHED();
152 return;
154 COMPILE_ASSERT(sizeof(base::MD5Digest) == 16, size_of_MD5_not_as_expected_);
155 base::MD5Digest md5_digest;
156 std::string user_sid_ascii(base::UTF16ToASCII(user_sid));
157 base::MD5Sum(user_sid_ascii.c_str(), user_sid_ascii.length(), &md5_digest);
158 const base::string16 base32_md5(
159 ShellUtil::ByteArrayToBase32(md5_digest.a, arraysize(md5_digest.a)));
160 // The value returned by the base32 algorithm above must never change and
161 // must always be 26 characters long (i.e. if someone ever moves this to
162 // base and implements the full base32 algorithm (i.e. with appended '='
163 // signs in the output), they must provide a flag to allow this method to
164 // still request the output with no appended '=' signs).
165 DCHECK_EQ(base32_md5.length(), 26U);
166 suffix_.reserve(base32_md5.length() + 1);
167 suffix_.assign(1, L'.');
168 suffix_.append(base32_md5);
171 bool UserSpecificRegistrySuffix::GetSuffix(base::string16* suffix) {
172 if (suffix_.empty()) {
173 NOTREACHED();
174 return false;
176 suffix->assign(suffix_);
177 return true;
180 // This class represents a single registry entry. The objective is to
181 // encapsulate all the registry entries required for registering Chrome at one
182 // place. This class can not be instantiated outside the class and the objects
183 // of this class type can be obtained only by calling a static method of this
184 // class.
185 class RegistryEntry {
186 public:
187 // A bit-field enum of places to look for this key in the Windows registry.
188 enum LookForIn {
189 LOOK_IN_HKCU = 1 << 0,
190 LOOK_IN_HKLM = 1 << 1,
191 LOOK_IN_HKCU_THEN_HKLM = LOOK_IN_HKCU | LOOK_IN_HKLM,
194 // Details about a Windows application, to be entered into the registry for
195 // the purpose of file associations.
196 struct ApplicationInfo {
197 ApplicationInfo() : file_type_icon_index(0), application_icon_index(0) {}
199 // The ProgId used by Windows for file associations with this application.
200 // Must not be empty or start with a '.'.
201 base::string16 prog_id;
202 // The friendly name, and the path of the icon that will be used for files
203 // of these types when associated with this application by default. (They
204 // are NOT the name/icon that will represent the application under the Open
205 // With menu.)
206 base::string16 file_type_name;
207 // TODO(mgiuca): |file_type_icon_path| should be a base::FilePath.
208 base::string16 file_type_icon_path;
209 int file_type_icon_index;
210 // The command to execute when opening a file via this association. It
211 // should contain "%1" (to tell Windows to pass the filename as an
212 // argument).
213 // TODO(mgiuca): |command_line| should be a base::CommandLine.
214 base::string16 command_line;
215 // The AppUserModelId used by Windows 8 for this application. Distinct from
216 // |prog_id|.
217 base::string16 app_id;
219 // User-visible details about this application. Any of these may be empty.
220 base::string16 application_name;
221 // TODO(mgiuca): |application_icon_path| should be a base::FilePath.
222 base::string16 application_icon_path;
223 int application_icon_index;
224 base::string16 application_description;
225 base::string16 publisher_name;
227 // The CLSID for the application's DelegateExecute handler. May be empty.
228 base::string16 delegate_clsid;
231 // Returns the Windows browser client registration key for Chrome. For
232 // example: "Software\Clients\StartMenuInternet\Chromium[.user]". Strictly
233 // speaking, we should use the name of the executable (e.g., "chrome.exe"),
234 // but that ship has sailed. The cost of switching now is re-prompting users
235 // to make Chrome their default browser, which isn't polite. |suffix| is the
236 // user-specific registration suffix; see GetUserSpecificDefaultBrowserSuffix
237 // in shell_util.h for details.
238 static base::string16 GetBrowserClientKey(BrowserDistribution* dist,
239 const base::string16& suffix) {
240 DCHECK(suffix.empty() || suffix[0] == L'.');
241 return base::string16(ShellUtil::kRegStartMenuInternet)
242 .append(1, L'\\')
243 .append(dist->GetBaseAppName())
244 .append(suffix);
247 // Returns the Windows Default Programs capabilities key for Chrome. For
248 // example:
249 // "Software\Clients\StartMenuInternet\Chromium[.user]\Capabilities".
250 static base::string16 GetCapabilitiesKey(BrowserDistribution* dist,
251 const base::string16& suffix) {
252 return GetBrowserClientKey(dist, suffix).append(L"\\Capabilities");
255 // This method returns a list of all the registry entries that
256 // are needed to register this installation's ProgId and AppId.
257 // These entries need to be registered in HKLM prior to Win8.
258 static void GetChromeProgIdEntries(BrowserDistribution* dist,
259 const base::string16& chrome_exe,
260 const base::string16& suffix,
261 ScopedVector<RegistryEntry>* entries) {
262 int chrome_icon_index =
263 dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME);
265 ApplicationInfo app_info;
266 app_info.prog_id = GetBrowserProgId(suffix);
267 app_info.file_type_name = dist->GetBrowserProgIdDesc();
268 // File types associated with Chrome are just given the Chrome icon.
269 app_info.file_type_icon_path = chrome_exe;
270 app_info.file_type_icon_index = chrome_icon_index;
271 app_info.command_line = ShellUtil::GetChromeShellOpenCmd(chrome_exe);
272 // For user-level installs: entries for the app id will be in HKCU; thus we
273 // do not need a suffix on those entries.
274 app_info.app_id = ShellUtil::GetBrowserModelId(
275 dist, InstallUtil::IsPerUserInstall(chrome_exe.c_str()));
277 // The command to execute when opening this application via the Metro UI.
278 base::string16 delegate_command(
279 ShellUtil::GetChromeDelegateCommand(chrome_exe));
280 bool set_delegate_execute =
281 IsChromeMetroSupported() &&
282 dist->GetCommandExecuteImplClsid(&app_info.delegate_clsid);
284 // TODO(grt): http://crbug.com/75152 Write a reference to a localized
285 // resource for name, description, and company.
286 app_info.application_name = dist->GetDisplayName();
287 app_info.application_icon_path = chrome_exe;
288 app_info.application_icon_index = chrome_icon_index;
289 app_info.application_description = dist->GetAppDescription();
290 app_info.publisher_name = dist->GetPublisherName();
292 GetProgIdEntries(app_info, entries);
294 // DelegateExecute ProgId. Needed for Chrome Metro in Windows 8. This is
295 // only needed for registring a web browser, not for general associations.
296 if (set_delegate_execute) {
297 base::string16 model_id_shell(ShellUtil::kRegClasses);
298 model_id_shell.push_back(base::FilePath::kSeparators[0]);
299 model_id_shell.append(app_info.app_id);
300 model_id_shell.append(ShellUtil::kRegExePath);
301 model_id_shell.append(ShellUtil::kRegShellPath);
303 // <root hkey>\Software\Classes\<app_id>\.exe\shell @=open
304 entries->push_back(new RegistryEntry(model_id_shell,
305 ShellUtil::kRegVerbOpen));
307 // Each of Chrome's shortcuts has an appid; which, as of Windows 8, is
308 // registered to handle some verbs. This registration has the side-effect
309 // that these verbs now show up in the shortcut's context menu. We
310 // mitigate this side-effect by making the context menu entries
311 // user readable/localized strings. See relevant MSDN article:
312 // http://msdn.microsoft.com/en-US/library/windows/desktop/cc144171.aspx
313 const struct {
314 const wchar_t* verb;
315 int name_id;
316 } verbs[] = {
317 { ShellUtil::kRegVerbOpen, -1 },
318 { ShellUtil::kRegVerbOpenNewWindow, IDS_SHORTCUT_NEW_WINDOW_BASE },
320 for (size_t i = 0; i < arraysize(verbs); ++i) {
321 base::string16 sub_path(model_id_shell);
322 sub_path.push_back(base::FilePath::kSeparators[0]);
323 sub_path.append(verbs[i].verb);
325 // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb>
326 if (verbs[i].name_id != -1) {
327 // TODO(grt): http://crbug.com/75152 Write a reference to a localized
328 // resource.
329 base::string16 verb_name(
330 installer::GetLocalizedString(verbs[i].name_id));
331 entries->push_back(new RegistryEntry(sub_path, verb_name.c_str()));
333 entries->push_back(new RegistryEntry(
334 sub_path, L"CommandId", L"Browser.Launch"));
336 sub_path.push_back(base::FilePath::kSeparators[0]);
337 sub_path.append(ShellUtil::kRegCommand);
339 // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb>\command
340 entries->push_back(new RegistryEntry(sub_path, delegate_command));
341 entries->push_back(new RegistryEntry(
342 sub_path, ShellUtil::kRegDelegateExecute, app_info.delegate_clsid));
347 // Gets the registry entries to register an application in the Windows
348 // registry. |app_info| provides all of the information needed.
349 static void GetProgIdEntries(const ApplicationInfo& app_info,
350 ScopedVector<RegistryEntry>* entries) {
351 // Basic sanity checks.
352 DCHECK(!app_info.prog_id.empty());
353 DCHECK_NE(L'.', app_info.prog_id[0]);
355 // File association ProgId
356 base::string16 prog_id_path(ShellUtil::kRegClasses);
357 prog_id_path.push_back(base::FilePath::kSeparators[0]);
358 prog_id_path.append(app_info.prog_id);
359 entries->push_back(
360 new RegistryEntry(prog_id_path, app_info.file_type_name));
361 entries->push_back(new RegistryEntry(
362 prog_id_path + ShellUtil::kRegDefaultIcon,
363 ShellUtil::FormatIconLocation(app_info.file_type_icon_path,
364 app_info.file_type_icon_index)));
365 entries->push_back(new RegistryEntry(
366 prog_id_path + ShellUtil::kRegShellOpen, app_info.command_line));
367 if (!app_info.delegate_clsid.empty()) {
368 entries->push_back(
369 new RegistryEntry(prog_id_path + ShellUtil::kRegShellOpen,
370 ShellUtil::kRegDelegateExecute,
371 app_info.delegate_clsid));
374 // The following entries are required as of Windows 8, but do not
375 // depend on the DelegateExecute verb handler being set.
376 if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
377 if (!app_info.app_id.empty()) {
378 entries->push_back(new RegistryEntry(
379 prog_id_path, ShellUtil::kRegAppUserModelId, app_info.app_id));
382 // Add \Software\Classes\<prog_id>\Application entries
383 base::string16 application_path(prog_id_path +
384 ShellUtil::kRegApplication);
385 if (!app_info.app_id.empty()) {
386 entries->push_back(new RegistryEntry(
387 application_path, ShellUtil::kRegAppUserModelId, app_info.app_id));
389 if (!app_info.application_icon_path.empty()) {
390 entries->push_back(new RegistryEntry(
391 application_path,
392 ShellUtil::kRegApplicationIcon,
393 ShellUtil::FormatIconLocation(app_info.application_icon_path,
394 app_info.application_icon_index)));
396 if (!app_info.application_name.empty()) {
397 entries->push_back(new RegistryEntry(application_path,
398 ShellUtil::kRegApplicationName,
399 app_info.application_name));
401 if (!app_info.application_description.empty()) {
402 entries->push_back(
403 new RegistryEntry(application_path,
404 ShellUtil::kRegApplicationDescription,
405 app_info.application_description));
407 if (!app_info.publisher_name.empty()) {
408 entries->push_back(new RegistryEntry(application_path,
409 ShellUtil::kRegApplicationCompany,
410 app_info.publisher_name));
415 // This method returns a list of the registry entries needed to declare a
416 // capability of handling a protocol on Windows.
417 static void GetProtocolCapabilityEntries(
418 BrowserDistribution* dist,
419 const base::string16& suffix,
420 const base::string16& protocol,
421 ScopedVector<RegistryEntry>* entries) {
422 entries->push_back(new RegistryEntry(
423 GetCapabilitiesKey(dist, suffix).append(L"\\URLAssociations"),
424 protocol, GetBrowserProgId(suffix)));
427 // This method returns a list of the registry entries required to register
428 // this installation in "RegisteredApplications" on Windows (to appear in
429 // Default Programs, StartMenuInternet, etc.).
430 // These entries need to be registered in HKLM prior to Win8.
431 // If |suffix| is not empty, these entries are guaranteed to be unique on this
432 // machine.
433 static void GetShellIntegrationEntries(BrowserDistribution* dist,
434 const base::string16& chrome_exe,
435 const base::string16& suffix,
436 ScopedVector<RegistryEntry>* entries) {
437 const base::string16 icon_path(
438 ShellUtil::FormatIconLocation(
439 chrome_exe,
440 dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME)));
441 const base::string16 quoted_exe_path(L"\"" + chrome_exe + L"\"");
443 // Register for the Start Menu "Internet" link (pre-Win7).
444 const base::string16 start_menu_entry(GetBrowserClientKey(dist, suffix));
445 // Register Chrome's display name.
446 // TODO(grt): http://crbug.com/75152 Also set LocalizedString; see
447 // http://msdn.microsoft.com/en-us/library/windows/desktop/cc144109(v=VS.85).aspx#registering_the_display_name
448 entries->push_back(new RegistryEntry(
449 start_menu_entry, dist->GetDisplayName()));
450 // Register the "open" verb for launching Chrome via the "Internet" link.
451 entries->push_back(new RegistryEntry(
452 start_menu_entry + ShellUtil::kRegShellOpen, quoted_exe_path));
453 // Register Chrome's icon for the Start Menu "Internet" link.
454 entries->push_back(new RegistryEntry(
455 start_menu_entry + ShellUtil::kRegDefaultIcon, icon_path));
457 // Register installation information.
458 base::string16 install_info(start_menu_entry + L"\\InstallInfo");
459 // Note: not using CommandLine since it has ambiguous rules for quoting
460 // strings.
461 entries->push_back(new RegistryEntry(install_info, kReinstallCommand,
462 quoted_exe_path + L" --" +
463 base::ASCIIToWide(switches::kMakeDefaultBrowser)));
464 entries->push_back(new RegistryEntry(install_info, L"HideIconsCommand",
465 quoted_exe_path + L" --" +
466 base::ASCIIToWide(switches::kHideIcons)));
467 entries->push_back(new RegistryEntry(install_info, L"ShowIconsCommand",
468 quoted_exe_path + L" --" +
469 base::ASCIIToWide(switches::kShowIcons)));
470 entries->push_back(new RegistryEntry(install_info, L"IconsVisible", 1));
472 // Register with Default Programs.
473 const base::string16 reg_app_name(dist->GetBaseAppName().append(suffix));
474 // Tell Windows where to find Chrome's Default Programs info.
475 const base::string16 capabilities(GetCapabilitiesKey(dist, suffix));
476 entries->push_back(new RegistryEntry(ShellUtil::kRegRegisteredApplications,
477 reg_app_name, capabilities));
478 // Write out Chrome's Default Programs info.
479 // TODO(grt): http://crbug.com/75152 Write a reference to a localized
480 // resource rather than this.
481 entries->push_back(new RegistryEntry(
482 capabilities, ShellUtil::kRegApplicationDescription,
483 dist->GetLongAppDescription()));
484 entries->push_back(new RegistryEntry(
485 capabilities, ShellUtil::kRegApplicationIcon, icon_path));
486 entries->push_back(new RegistryEntry(
487 capabilities, ShellUtil::kRegApplicationName,
488 dist->GetDisplayName()));
490 entries->push_back(new RegistryEntry(capabilities + L"\\Startmenu",
491 L"StartMenuInternet", reg_app_name));
493 const base::string16 html_prog_id(GetBrowserProgId(suffix));
494 for (int i = 0; ShellUtil::kPotentialFileAssociations[i] != NULL; i++) {
495 entries->push_back(new RegistryEntry(
496 capabilities + L"\\FileAssociations",
497 ShellUtil::kPotentialFileAssociations[i], html_prog_id));
499 for (int i = 0; ShellUtil::kPotentialProtocolAssociations[i] != NULL;
500 i++) {
501 entries->push_back(new RegistryEntry(
502 capabilities + L"\\URLAssociations",
503 ShellUtil::kPotentialProtocolAssociations[i], html_prog_id));
507 // This method returns a list of the registry entries required for this
508 // installation to be registered in the Windows shell.
509 // In particular:
510 // - App Paths
511 // http://msdn.microsoft.com/en-us/library/windows/desktop/ee872121
512 // - File Associations
513 // http://msdn.microsoft.com/en-us/library/bb166549
514 // These entries need to be registered in HKLM prior to Win8.
515 static void GetChromeAppRegistrationEntries(
516 const base::string16& chrome_exe,
517 const base::string16& suffix,
518 ScopedVector<RegistryEntry>* entries) {
519 const base::FilePath chrome_path(chrome_exe);
520 base::string16 app_path_key(ShellUtil::kAppPathsRegistryKey);
521 app_path_key.push_back(base::FilePath::kSeparators[0]);
522 app_path_key.append(chrome_path.BaseName().value());
523 entries->push_back(new RegistryEntry(app_path_key, chrome_exe));
524 entries->push_back(new RegistryEntry(app_path_key,
525 ShellUtil::kAppPathsRegistryPathName, chrome_path.DirName().value()));
527 const base::string16 html_prog_id(GetBrowserProgId(suffix));
528 for (int i = 0; ShellUtil::kPotentialFileAssociations[i] != NULL; i++) {
529 GetAppExtRegistrationEntries(
530 html_prog_id, ShellUtil::kPotentialFileAssociations[i], entries);
534 // Gets the registry entries to register an application as a handler for a
535 // particular file extension. |prog_id| is the ProgId used by Windows for the
536 // application. |ext| is the file extension, which must begin with a '.'.
537 static void GetAppExtRegistrationEntries(
538 const base::string16& prog_id,
539 const base::string16& ext,
540 ScopedVector<RegistryEntry>* entries) {
541 // In HKEY_CURRENT_USER\Software\Classes\EXT\OpenWithProgids, create an
542 // empty value with this class's ProgId.
543 base::string16 key_name(ShellUtil::kRegClasses);
544 key_name.push_back(base::FilePath::kSeparators[0]);
545 key_name.append(ext);
546 key_name.push_back(base::FilePath::kSeparators[0]);
547 key_name.append(ShellUtil::kRegOpenWithProgids);
548 entries->push_back(new RegistryEntry(key_name, prog_id, base::string16()));
551 // Gets the registry entries to register an application as the default handler
552 // for a particular file extension. |prog_id| is the ProgId used by Windows
553 // for the application. |ext| is the file extension, which must begin with a
554 // '.'. If |overwrite_existing|, always sets the default handler; otherwise
555 // only sets if there is no existing default.
557 // This has no effect on Windows 8. Windows 8 ignores the default and lets the
558 // user choose. If there is only one handler for a file, it will automatically
559 // become the default. Otherwise, the first time the user opens a file, they
560 // are presented with the dialog to set the default handler. (This is roughly
561 // equivalent to being called with |overwrite_existing| false.)
562 static void GetAppDefaultRegistrationEntries(
563 const base::string16& prog_id,
564 const base::string16& ext,
565 bool overwrite_existing,
566 ScopedVector<RegistryEntry>* entries) {
567 // Set the default value of HKEY_CURRENT_USER\Software\Classes\EXT to this
568 // class's name.
569 base::string16 key_name(ShellUtil::kRegClasses);
570 key_name.push_back(base::FilePath::kSeparators[0]);
571 key_name.append(ext);
572 scoped_ptr<RegistryEntry> default_association(
573 new RegistryEntry(key_name, prog_id));
574 if (overwrite_existing ||
575 !default_association->KeyExistsInRegistry(
576 RegistryEntry::LOOK_IN_HKCU)) {
577 entries->push_back(default_association.release());
581 // This method returns a list of all the user level registry entries that
582 // are needed to make Chromium the default handler for a protocol on XP.
583 static void GetXPStyleUserProtocolEntries(
584 const base::string16& protocol,
585 const base::string16& chrome_icon,
586 const base::string16& chrome_open,
587 ScopedVector<RegistryEntry>* entries) {
588 // Protocols associations.
589 base::string16 url_key(ShellUtil::kRegClasses);
590 url_key.push_back(base::FilePath::kSeparators[0]);
591 url_key.append(protocol);
593 // This registry value tells Windows that this 'class' is a URL scheme
594 // so IE, explorer and other apps will route it to our handler.
595 // <root hkey>\Software\Classes\<protocol>\URL Protocol
596 entries->push_back(new RegistryEntry(url_key,
597 ShellUtil::kRegUrlProtocol, base::string16()));
599 // <root hkey>\Software\Classes\<protocol>\DefaultIcon
600 base::string16 icon_key = url_key + ShellUtil::kRegDefaultIcon;
601 entries->push_back(new RegistryEntry(icon_key, chrome_icon));
603 // <root hkey>\Software\Classes\<protocol>\shell\open\command
604 base::string16 shell_key = url_key + ShellUtil::kRegShellOpen;
605 entries->push_back(new RegistryEntry(shell_key, chrome_open));
607 // <root hkey>\Software\Classes\<protocol>\shell\open\ddeexec
608 base::string16 dde_key = url_key + L"\\shell\\open\\ddeexec";
609 entries->push_back(new RegistryEntry(dde_key, base::string16()));
611 // <root hkey>\Software\Classes\<protocol>\shell\@
612 base::string16 protocol_shell_key = url_key + ShellUtil::kRegShellPath;
613 entries->push_back(new RegistryEntry(protocol_shell_key, L"open"));
616 // This method returns a list of all the user level registry entries that
617 // are needed to make Chromium default browser on XP.
618 // Some of these entries are irrelevant in recent versions of Windows, but
619 // we register them anyways as some legacy apps are hardcoded to lookup those
620 // values.
621 static void GetXPStyleDefaultBrowserUserEntries(
622 BrowserDistribution* dist,
623 const base::string16& chrome_exe,
624 const base::string16& suffix,
625 ScopedVector<RegistryEntry>* entries) {
626 // File extension associations.
627 base::string16 html_prog_id(GetBrowserProgId(suffix));
628 for (int i = 0; ShellUtil::kDefaultFileAssociations[i] != NULL; i++) {
629 GetAppDefaultRegistrationEntries(
630 html_prog_id, ShellUtil::kDefaultFileAssociations[i], true, entries);
633 // Protocols associations.
634 base::string16 chrome_open = ShellUtil::GetChromeShellOpenCmd(chrome_exe);
635 base::string16 chrome_icon =
636 ShellUtil::FormatIconLocation(
637 chrome_exe,
638 dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME));
639 for (int i = 0; ShellUtil::kBrowserProtocolAssociations[i] != NULL; i++) {
640 GetXPStyleUserProtocolEntries(ShellUtil::kBrowserProtocolAssociations[i],
641 chrome_icon, chrome_open, entries);
644 // start->Internet shortcut.
645 base::string16 start_menu(ShellUtil::kRegStartMenuInternet);
646 base::string16 app_name = dist->GetBaseAppName() + suffix;
647 entries->push_back(new RegistryEntry(start_menu, app_name));
650 // Generate work_item tasks required to create current registry entry and
651 // add them to the given work item list.
652 void AddToWorkItemList(HKEY root, WorkItemList *items) const {
653 items->AddCreateRegKeyWorkItem(root, key_path_, WorkItem::kWow64Default);
654 if (is_string_) {
655 items->AddSetRegValueWorkItem(
656 root, key_path_, WorkItem::kWow64Default, name_, value_, true);
657 } else {
658 items->AddSetRegValueWorkItem(
659 root, key_path_, WorkItem::kWow64Default, name_, int_value_, true);
663 // Checks if the current registry entry exists in HKCU\|key_path_|\|name_|
664 // and value is |value_|. If the key does NOT exist in HKCU, checks for
665 // the correct name and value in HKLM.
666 // |look_for_in| specifies roots (HKCU and/or HKLM) in which to look for the
667 // key, unspecified roots are not looked into (i.e. the the key is assumed not
668 // to exist in them).
669 // |look_for_in| must at least specify one root to look into.
670 // If |look_for_in| is LOOK_IN_HKCU_THEN_HKLM, this method mimics Windows'
671 // behavior when searching in HKCR (HKCU takes precedence over HKLM). For
672 // registrations outside of HKCR on versions of Windows prior to Win8,
673 // Chrome's values go in HKLM. This function will make unnecessary (but
674 // harmless) queries into HKCU in that case.
675 bool ExistsInRegistry(uint32 look_for_in) const {
676 DCHECK(look_for_in);
678 RegistryStatus status = DOES_NOT_EXIST;
679 if (look_for_in & LOOK_IN_HKCU)
680 status = StatusInRegistryUnderRoot(HKEY_CURRENT_USER);
681 if (status == DOES_NOT_EXIST && (look_for_in & LOOK_IN_HKLM))
682 status = StatusInRegistryUnderRoot(HKEY_LOCAL_MACHINE);
683 return status == SAME_VALUE;
686 // Checks if the current registry entry exists in \|key_path_|\|name_|,
687 // regardless of value. Same lookup rules as ExistsInRegistry.
688 // Unlike ExistsInRegistry, this returns true if some other value is present
689 // with the same key.
690 bool KeyExistsInRegistry(uint32 look_for_in) const {
691 DCHECK(look_for_in);
693 RegistryStatus status = DOES_NOT_EXIST;
694 if (look_for_in & LOOK_IN_HKCU)
695 status = StatusInRegistryUnderRoot(HKEY_CURRENT_USER);
696 if (status == DOES_NOT_EXIST && (look_for_in & LOOK_IN_HKLM))
697 status = StatusInRegistryUnderRoot(HKEY_LOCAL_MACHINE);
698 return status != DOES_NOT_EXIST;
701 private:
702 // States this RegistryKey can be in compared to the registry.
703 enum RegistryStatus {
704 // |name_| does not exist in the registry
705 DOES_NOT_EXIST,
706 // |name_| exists, but its value != |value_|
707 DIFFERENT_VALUE,
708 // |name_| exists and its value is |value_|
709 SAME_VALUE,
712 // Create a object that represent default value of a key
713 RegistryEntry(const base::string16& key_path, const base::string16& value)
714 : key_path_(key_path), name_(),
715 is_string_(true), value_(value), int_value_(0) {
718 // Create a object that represent a key of type REG_SZ
719 RegistryEntry(const base::string16& key_path, const base::string16& name,
720 const base::string16& value)
721 : key_path_(key_path), name_(name),
722 is_string_(true), value_(value), int_value_(0) {
725 // Create a object that represent a key of integer type
726 RegistryEntry(const base::string16& key_path, const base::string16& name,
727 DWORD value)
728 : key_path_(key_path), name_(name),
729 is_string_(false), value_(), int_value_(value) {
732 base::string16 key_path_; // key path for the registry entry
733 base::string16 name_; // name of the registry entry
734 bool is_string_; // true if current registry entry is of type REG_SZ
735 base::string16 value_; // string value (useful if is_string_ = true)
736 DWORD int_value_; // integer value (useful if is_string_ = false)
738 // Helper function for ExistsInRegistry().
739 // Returns the RegistryStatus of the current registry entry in
740 // |root|\|key_path_|\|name_|.
741 RegistryStatus StatusInRegistryUnderRoot(HKEY root) const {
742 RegKey key(root, key_path_.c_str(), KEY_QUERY_VALUE);
743 bool found = false;
744 bool correct_value = false;
745 if (is_string_) {
746 base::string16 read_value;
747 found = key.ReadValue(name_.c_str(), &read_value) == ERROR_SUCCESS;
748 if (found) {
749 correct_value = read_value.size() == value_.size() &&
750 std::equal(value_.begin(), value_.end(), read_value.begin(),
751 base::CaseInsensitiveCompare<wchar_t>());
753 } else {
754 DWORD read_value;
755 found = key.ReadValueDW(name_.c_str(), &read_value) == ERROR_SUCCESS;
756 if (found)
757 correct_value = read_value == int_value_;
759 return found ?
760 (correct_value ? SAME_VALUE : DIFFERENT_VALUE) : DOES_NOT_EXIST;
763 DISALLOW_COPY_AND_ASSIGN(RegistryEntry);
764 }; // class RegistryEntry
767 // This method converts all the RegistryEntries from the given list to
768 // Set/CreateRegWorkItems and runs them using WorkItemList.
769 bool AddRegistryEntries(HKEY root, const ScopedVector<RegistryEntry>& entries) {
770 scoped_ptr<WorkItemList> items(WorkItem::CreateWorkItemList());
772 for (ScopedVector<RegistryEntry>::const_iterator itr = entries.begin();
773 itr != entries.end(); ++itr)
774 (*itr)->AddToWorkItemList(root, items.get());
776 // Apply all the registry changes and if there is a problem, rollback
777 if (!items->Do()) {
778 items->Rollback();
779 return false;
781 return true;
784 // Checks that all |entries| are present on this computer.
785 // |look_for_in| is passed to RegistryEntry::ExistsInRegistry(). Documentation
786 // for it can be found there.
787 bool AreEntriesRegistered(const ScopedVector<RegistryEntry>& entries,
788 uint32 look_for_in) {
789 bool registered = true;
790 for (ScopedVector<RegistryEntry>::const_iterator itr = entries.begin();
791 registered && itr != entries.end(); ++itr) {
792 // We do not need registered = registered && ... since the loop condition
793 // is set to exit early.
794 registered = (*itr)->ExistsInRegistry(look_for_in);
796 return registered;
799 // Checks that all required registry entries for Chrome are already present
800 // on this computer. See RegistryEntry::ExistsInRegistry for the behavior of
801 // |look_for_in|.
802 // Note: between r133333 and r154145 we were registering parts of Chrome in HKCU
803 // and parts in HKLM for user-level installs; we now always register everything
804 // under a single registry root. Not doing so caused http://crbug.com/144910 for
805 // users who first-installed Chrome in that revision range (those users are
806 // still impacted by http://crbug.com/144910). This method will keep returning
807 // true for affected users (i.e. who have all the registrations, but over both
808 // registry roots).
809 bool IsChromeRegistered(BrowserDistribution* dist,
810 const base::string16& chrome_exe,
811 const base::string16& suffix,
812 uint32 look_for_in) {
813 ScopedVector<RegistryEntry> entries;
814 RegistryEntry::GetChromeProgIdEntries(dist, chrome_exe, suffix, &entries);
815 RegistryEntry::GetShellIntegrationEntries(dist, chrome_exe, suffix, &entries);
816 RegistryEntry::GetChromeAppRegistrationEntries(chrome_exe, suffix, &entries);
817 return AreEntriesRegistered(entries, look_for_in);
820 // This method checks if Chrome is already registered on the local machine
821 // for the requested protocol. It just checks the one value required for this.
822 // See RegistryEntry::ExistsInRegistry for the behavior of |look_for_in|.
823 bool IsChromeRegisteredForProtocol(BrowserDistribution* dist,
824 const base::string16& suffix,
825 const base::string16& protocol,
826 uint32 look_for_in) {
827 ScopedVector<RegistryEntry> entries;
828 RegistryEntry::GetProtocolCapabilityEntries(dist, suffix, protocol, &entries);
829 return AreEntriesRegistered(entries, look_for_in);
832 // This method registers Chrome on Vista by launching an elevated setup.exe.
833 // That will show the user the standard Vista elevation prompt. If the user
834 // accepts it the new process will make the necessary changes and return SUCCESS
835 // that we capture and return.
836 // If protocol is non-empty we will also register Chrome as being capable of
837 // handling the protocol.
838 bool ElevateAndRegisterChrome(BrowserDistribution* dist,
839 const base::string16& chrome_exe,
840 const base::string16& suffix,
841 const base::string16& protocol) {
842 // Only user-level installs prior to Windows 8 should need to elevate to
843 // register.
844 DCHECK(InstallUtil::IsPerUserInstall(chrome_exe.c_str()));
845 DCHECK_LT(base::win::GetVersion(), base::win::VERSION_WIN8);
846 base::FilePath exe_path =
847 base::FilePath(chrome_exe).DirName().Append(installer::kSetupExe);
848 if (!base::PathExists(exe_path)) {
849 HKEY reg_root = InstallUtil::IsPerUserInstall(chrome_exe.c_str()) ?
850 HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
851 RegKey key(reg_root,
852 dist->GetUninstallRegPath().c_str(),
853 KEY_READ | KEY_WOW64_32KEY);
854 base::string16 uninstall_string;
855 key.ReadValue(installer::kUninstallStringField, &uninstall_string);
856 CommandLine command_line = CommandLine::FromString(uninstall_string);
857 exe_path = command_line.GetProgram();
860 if (base::PathExists(exe_path)) {
861 CommandLine cmd(exe_path);
862 cmd.AppendSwitchNative(installer::switches::kRegisterChromeBrowser,
863 chrome_exe);
864 if (!suffix.empty()) {
865 cmd.AppendSwitchNative(
866 installer::switches::kRegisterChromeBrowserSuffix, suffix);
869 if (!protocol.empty()) {
870 cmd.AppendSwitchNative(
871 installer::switches::kRegisterURLProtocol, protocol);
874 DWORD ret_val = 0;
875 InstallUtil::ExecuteExeAsAdmin(cmd, &ret_val);
876 if (ret_val == 0)
877 return true;
879 return false;
882 // Launches the Windows 7 and Windows 8 dialog for picking the application to
883 // handle the given protocol. Most importantly, this is used to set the default
884 // handler for http (and, implicitly with it, https). In that case it is also
885 // known as the 'how do you want to open webpages' dialog.
886 // It is required that Chrome be already *registered* for the given protocol.
887 bool LaunchSelectDefaultProtocolHandlerDialog(const wchar_t* protocol) {
888 DCHECK(protocol);
889 OPENASINFO open_as_info = {};
890 open_as_info.pcszFile = protocol;
891 open_as_info.oaifInFlags =
892 OAIF_URL_PROTOCOL | OAIF_FORCE_REGISTRATION | OAIF_REGISTER_EXT;
893 HRESULT hr = SHOpenWithDialog(NULL, &open_as_info);
894 DLOG_IF(WARNING, FAILED(hr)) << "Failed to set as default " << protocol
895 << " handler; hr=0x" << std::hex << hr;
896 if (FAILED(hr))
897 return false;
898 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
899 return true;
902 // Launches the Windows 7 and Windows 8 application association dialog, which
903 // is the only documented way to make a browser the default browser on
904 // Windows 8.
905 bool LaunchApplicationAssociationDialog(const base::string16& app_id) {
906 base::win::ScopedComPtr<IApplicationAssociationRegistrationUI> aarui;
907 HRESULT hr = aarui.CreateInstance(CLSID_ApplicationAssociationRegistrationUI);
908 if (FAILED(hr))
909 return false;
910 hr = aarui->LaunchAdvancedAssociationUI(app_id.c_str());
911 return SUCCEEDED(hr);
914 // Returns true if the current install's |chrome_exe| has been registered with
915 // |suffix|.
916 // |confirmation_level| is the level of verification desired as described in
917 // the RegistrationConfirmationLevel enum above.
918 // |suffix| can be the empty string (this is used to support old installs
919 // where we used to not suffix user-level installs if they were the first to
920 // request the non-suffixed registry entries on the machine).
921 // NOTE: This a quick check that only validates that a single registry entry
922 // points to |chrome_exe|. This should only be used at run-time to determine
923 // how Chrome is registered, not to know whether the registration is complete
924 // at install-time (IsChromeRegistered() can be used for that).
925 bool QuickIsChromeRegistered(BrowserDistribution* dist,
926 const base::string16& chrome_exe,
927 const base::string16& suffix,
928 RegistrationConfirmationLevel confirmation_level) {
929 // Get the appropriate key to look for based on the level desired.
930 base::string16 reg_key;
931 switch (confirmation_level) {
932 case CONFIRM_PROGID_REGISTRATION:
933 // Software\Classes\ChromeHTML|suffix|
934 reg_key = ShellUtil::kRegClasses;
935 reg_key.push_back(base::FilePath::kSeparators[0]);
936 reg_key.append(dist->GetBrowserProgIdPrefix());
937 reg_key.append(suffix);
938 break;
939 case CONFIRM_SHELL_REGISTRATION:
940 case CONFIRM_SHELL_REGISTRATION_IN_HKLM:
941 // Software\Clients\StartMenuInternet\Google Chrome|suffix|
942 reg_key = RegistryEntry::GetBrowserClientKey(dist, suffix);
943 break;
944 default:
945 NOTREACHED();
946 break;
948 reg_key.append(ShellUtil::kRegShellOpen);
950 // ProgId registrations are allowed to reside in HKCU for user-level installs
951 // (and values there have priority over values in HKLM). The same is true for
952 // shell integration entries as of Windows 8.
953 if (confirmation_level == CONFIRM_PROGID_REGISTRATION ||
954 (confirmation_level == CONFIRM_SHELL_REGISTRATION &&
955 base::win::GetVersion() >= base::win::VERSION_WIN8)) {
956 const RegKey key_hkcu(HKEY_CURRENT_USER, reg_key.c_str(), KEY_QUERY_VALUE);
957 base::string16 hkcu_value;
958 // If |reg_key| is present in HKCU, assert that it points to |chrome_exe|.
959 // Otherwise, fall back on an HKLM lookup below.
960 if (key_hkcu.ReadValue(L"", &hkcu_value) == ERROR_SUCCESS) {
961 return InstallUtil::ProgramCompare(
962 base::FilePath(chrome_exe)).Evaluate(hkcu_value);
966 // Assert that |reg_key| points to |chrome_exe| in HKLM.
967 const RegKey key_hklm(HKEY_LOCAL_MACHINE, reg_key.c_str(), KEY_QUERY_VALUE);
968 base::string16 hklm_value;
969 if (key_hklm.ReadValue(L"", &hklm_value) == ERROR_SUCCESS) {
970 return InstallUtil::ProgramCompare(
971 base::FilePath(chrome_exe)).Evaluate(hklm_value);
973 return false;
976 // Sets |suffix| to a 27 character string that is specific to this user on this
977 // machine (on user-level installs only).
978 // To support old-style user-level installs however, |suffix| is cleared if the
979 // user currently owns the non-suffixed HKLM registrations.
980 // |suffix| can also be set to the user's username if the current install is
981 // suffixed as per the old-style registrations.
982 // |suffix| is cleared on system-level installs.
983 // |suffix| should then be appended to all Chrome properties that may conflict
984 // with other Chrome user-level installs.
985 // Returns true unless one of the underlying calls fails.
986 bool GetInstallationSpecificSuffix(BrowserDistribution* dist,
987 const base::string16& chrome_exe,
988 base::string16* suffix) {
989 if (!InstallUtil::IsPerUserInstall(chrome_exe.c_str()) ||
990 QuickIsChromeRegistered(dist, chrome_exe, base::string16(),
991 CONFIRM_SHELL_REGISTRATION)) {
992 // No suffix on system-level installs and user-level installs already
993 // registered with no suffix.
994 suffix->clear();
995 return true;
998 // Get the old suffix for the check below.
999 if (!ShellUtil::GetOldUserSpecificRegistrySuffix(suffix)) {
1000 NOTREACHED();
1001 return false;
1003 if (QuickIsChromeRegistered(dist, chrome_exe, *suffix,
1004 CONFIRM_SHELL_REGISTRATION)) {
1005 // Username suffix for installs that are suffixed as per the old-style.
1006 return true;
1009 return ShellUtil::GetUserSpecificRegistrySuffix(suffix);
1012 // Returns the root registry key (HKLM or HKCU) under which registrations must
1013 // be placed for this install. As of Windows 8 everything can go in HKCU for
1014 // per-user installs.
1015 HKEY DetermineRegistrationRoot(bool is_per_user) {
1016 return is_per_user && base::win::GetVersion() >= base::win::VERSION_WIN8 ?
1017 HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
1020 // Associates Chrome with supported protocols and file associations. This should
1021 // not be required on Vista+ but since some applications still read
1022 // Software\Classes\http key directly, we have to do this on Vista+ as well.
1023 bool RegisterChromeAsDefaultXPStyle(BrowserDistribution* dist,
1024 int shell_change,
1025 const base::string16& chrome_exe) {
1026 bool ret = true;
1027 ScopedVector<RegistryEntry> entries;
1028 RegistryEntry::GetXPStyleDefaultBrowserUserEntries(
1029 dist, chrome_exe,
1030 ShellUtil::GetCurrentInstallationSuffix(dist, chrome_exe), &entries);
1032 // Change the default browser for current user.
1033 if ((shell_change & ShellUtil::CURRENT_USER) &&
1034 !AddRegistryEntries(HKEY_CURRENT_USER, entries)) {
1035 ret = false;
1036 LOG(ERROR) << "Could not make Chrome default browser (XP/current user).";
1039 // Chrome as default browser at system level.
1040 if ((shell_change & ShellUtil::SYSTEM_LEVEL) &&
1041 !AddRegistryEntries(HKEY_LOCAL_MACHINE, entries)) {
1042 ret = false;
1043 LOG(ERROR) << "Could not make Chrome default browser (XP/system level).";
1046 return ret;
1049 // Associates Chrome with |protocol| in the registry. This should not be
1050 // required on Vista+ but since some applications still read these registry
1051 // keys directly, we have to do this on Vista+ as well.
1052 // See http://msdn.microsoft.com/library/aa767914.aspx for more details.
1053 bool RegisterChromeAsDefaultProtocolClientXPStyle(
1054 BrowserDistribution* dist,
1055 const base::string16& chrome_exe,
1056 const base::string16& protocol) {
1057 ScopedVector<RegistryEntry> entries;
1058 const base::string16 chrome_open(
1059 ShellUtil::GetChromeShellOpenCmd(chrome_exe));
1060 const base::string16 chrome_icon(
1061 ShellUtil::FormatIconLocation(
1062 chrome_exe,
1063 dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME)));
1064 RegistryEntry::GetXPStyleUserProtocolEntries(protocol, chrome_icon,
1065 chrome_open, &entries);
1066 // Change the default protocol handler for current user.
1067 if (!AddRegistryEntries(HKEY_CURRENT_USER, entries)) {
1068 LOG(ERROR) << "Could not make Chrome default protocol client (XP).";
1069 return false;
1072 return true;
1075 // Returns |properties.shortcut_name| if the property is set, otherwise it
1076 // returns dist->GetShortcutName(BrowserDistribution::SHORTCUT_CHROME). In any
1077 // case, it makes sure the return value is suffixed with ".lnk".
1078 base::string16 ExtractShortcutNameFromProperties(
1079 BrowserDistribution* dist,
1080 const ShellUtil::ShortcutProperties& properties) {
1081 DCHECK(dist);
1082 base::string16 shortcut_name;
1083 if (properties.has_shortcut_name()) {
1084 shortcut_name = properties.shortcut_name;
1085 } else {
1086 shortcut_name =
1087 dist->GetShortcutName(BrowserDistribution::SHORTCUT_CHROME);
1090 if (!EndsWith(shortcut_name, installer::kLnkExt, false))
1091 shortcut_name.append(installer::kLnkExt);
1093 return shortcut_name;
1096 // Converts ShellUtil::ShortcutOperation to the best-matching value in
1097 // base::win::ShortcutOperation.
1098 base::win::ShortcutOperation TranslateShortcutOperation(
1099 ShellUtil::ShortcutOperation operation) {
1100 switch (operation) {
1101 case ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS: // Falls through.
1102 case ShellUtil::SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL:
1103 return base::win::SHORTCUT_CREATE_ALWAYS;
1105 case ShellUtil::SHELL_SHORTCUT_UPDATE_EXISTING:
1106 return base::win::SHORTCUT_UPDATE_EXISTING;
1108 case ShellUtil::SHELL_SHORTCUT_REPLACE_EXISTING:
1109 return base::win::SHORTCUT_REPLACE_EXISTING;
1111 default:
1112 NOTREACHED();
1113 return base::win::SHORTCUT_REPLACE_EXISTING;
1117 // Returns a base::win::ShortcutProperties struct containing the properties
1118 // to set on the shortcut based on the provided ShellUtil::ShortcutProperties.
1119 base::win::ShortcutProperties TranslateShortcutProperties(
1120 const ShellUtil::ShortcutProperties& properties) {
1121 base::win::ShortcutProperties shortcut_properties;
1123 if (properties.has_target()) {
1124 shortcut_properties.set_target(properties.target);
1125 DCHECK(!properties.target.DirName().empty());
1126 shortcut_properties.set_working_dir(properties.target.DirName());
1129 if (properties.has_arguments())
1130 shortcut_properties.set_arguments(properties.arguments);
1132 if (properties.has_description())
1133 shortcut_properties.set_description(properties.description);
1135 if (properties.has_icon())
1136 shortcut_properties.set_icon(properties.icon, properties.icon_index);
1138 if (properties.has_app_id())
1139 shortcut_properties.set_app_id(properties.app_id);
1141 if (properties.has_dual_mode())
1142 shortcut_properties.set_dual_mode(properties.dual_mode);
1144 return shortcut_properties;
1147 // Cleans up an old verb (run) we used to register in
1148 // <root>\Software\Classes\Chrome<.suffix>\.exe\shell\run on Windows 8.
1149 void RemoveRunVerbOnWindows8(BrowserDistribution* dist,
1150 const base::string16& chrome_exe) {
1151 if (IsChromeMetroSupported()) {
1152 bool is_per_user_install =InstallUtil::IsPerUserInstall(chrome_exe.c_str());
1153 HKEY root_key = DetermineRegistrationRoot(is_per_user_install);
1154 // There's no need to rollback, so forgo the usual work item lists and just
1155 // remove the key from the registry.
1156 base::string16 run_verb_key(ShellUtil::kRegClasses);
1157 run_verb_key.push_back(base::FilePath::kSeparators[0]);
1158 run_verb_key.append(ShellUtil::GetBrowserModelId(
1159 dist, is_per_user_install));
1160 run_verb_key.append(ShellUtil::kRegExePath);
1161 run_verb_key.append(ShellUtil::kRegShellPath);
1162 run_verb_key.push_back(base::FilePath::kSeparators[0]);
1163 run_verb_key.append(ShellUtil::kRegVerbRun);
1164 InstallUtil::DeleteRegistryKey(root_key, run_verb_key,
1165 WorkItem::kWow64Default);
1169 // Gets the short (8.3) form of |path|, putting the result in |short_path| and
1170 // returning true on success. |short_path| is not modified on failure.
1171 bool ShortNameFromPath(const base::FilePath& path, base::string16* short_path) {
1172 DCHECK(short_path);
1173 base::string16 result(MAX_PATH, L'\0');
1174 DWORD short_length = GetShortPathName(path.value().c_str(), &result[0],
1175 result.size());
1176 if (short_length == 0 || short_length > result.size()) {
1177 PLOG(ERROR) << "Error getting short (8.3) path";
1178 return false;
1181 result.resize(short_length);
1182 short_path->swap(result);
1183 return true;
1186 // Probe using IApplicationAssociationRegistration::QueryCurrentDefault
1187 // (Windows 8); see ProbeProtocolHandlers. This mechanism is not suitable for
1188 // use on previous versions of Windows despite the presence of
1189 // QueryCurrentDefault on them since versions of Windows prior to Windows 8
1190 // did not perform validation on the ProgID registered as the current default.
1191 // As a result, stale ProgIDs could be returned, leading to false positives.
1192 ShellUtil::DefaultState ProbeCurrentDefaultHandlers(
1193 const base::FilePath& chrome_exe,
1194 const wchar_t* const* protocols,
1195 size_t num_protocols) {
1196 base::win::ScopedComPtr<IApplicationAssociationRegistration> registration;
1197 HRESULT hr = registration.CreateInstance(
1198 CLSID_ApplicationAssociationRegistration, NULL, CLSCTX_INPROC);
1199 if (FAILED(hr))
1200 return ShellUtil::UNKNOWN_DEFAULT;
1202 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
1203 base::string16 prog_id(dist->GetBrowserProgIdPrefix());
1204 prog_id += ShellUtil::GetCurrentInstallationSuffix(dist, chrome_exe.value());
1206 for (size_t i = 0; i < num_protocols; ++i) {
1207 base::win::ScopedCoMem<wchar_t> current_app;
1208 hr = registration->QueryCurrentDefault(protocols[i], AT_URLPROTOCOL,
1209 AL_EFFECTIVE, &current_app);
1210 if (FAILED(hr) || prog_id.compare(current_app) != 0)
1211 return ShellUtil::NOT_DEFAULT;
1214 return ShellUtil::IS_DEFAULT;
1217 // Probe using IApplicationAssociationRegistration::QueryAppIsDefault (Vista and
1218 // Windows 7); see ProbeProtocolHandlers.
1219 ShellUtil::DefaultState ProbeAppIsDefaultHandlers(
1220 const base::FilePath& chrome_exe,
1221 const wchar_t* const* protocols,
1222 size_t num_protocols) {
1223 base::win::ScopedComPtr<IApplicationAssociationRegistration> registration;
1224 HRESULT hr = registration.CreateInstance(
1225 CLSID_ApplicationAssociationRegistration, NULL, CLSCTX_INPROC);
1226 if (FAILED(hr))
1227 return ShellUtil::UNKNOWN_DEFAULT;
1229 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
1230 base::string16 app_name(
1231 ShellUtil::GetApplicationName(dist, chrome_exe.value()));
1233 BOOL result;
1234 for (size_t i = 0; i < num_protocols; ++i) {
1235 result = TRUE;
1236 hr = registration->QueryAppIsDefault(protocols[i], AT_URLPROTOCOL,
1237 AL_EFFECTIVE, app_name.c_str(), &result);
1238 if (FAILED(hr) || result == FALSE)
1239 return ShellUtil::NOT_DEFAULT;
1242 return ShellUtil::IS_DEFAULT;
1245 // Probe the current commands registered to handle the shell "open" verb for
1246 // |protocols| (Windows XP); see ProbeProtocolHandlers.
1247 ShellUtil::DefaultState ProbeOpenCommandHandlers(
1248 const base::FilePath& chrome_exe,
1249 const wchar_t* const* protocols,
1250 size_t num_protocols) {
1251 // Get its short (8.3) form.
1252 base::string16 short_app_path;
1253 if (!ShortNameFromPath(chrome_exe, &short_app_path))
1254 return ShellUtil::UNKNOWN_DEFAULT;
1256 const HKEY root_key = HKEY_CLASSES_ROOT;
1257 base::string16 key_path;
1258 base::win::RegKey key;
1259 base::string16 value;
1260 CommandLine command_line(CommandLine::NO_PROGRAM);
1261 base::string16 short_path;
1263 for (size_t i = 0; i < num_protocols; ++i) {
1264 // Get the command line from HKCU\<protocol>\shell\open\command.
1265 key_path.assign(protocols[i]).append(ShellUtil::kRegShellOpen);
1266 if ((key.Open(root_key, key_path.c_str(),
1267 KEY_QUERY_VALUE) != ERROR_SUCCESS) ||
1268 (key.ReadValue(L"", &value) != ERROR_SUCCESS)) {
1269 return ShellUtil::NOT_DEFAULT;
1272 // Need to normalize path in case it's been munged.
1273 command_line = CommandLine::FromString(value);
1274 if (!ShortNameFromPath(command_line.GetProgram(), &short_path))
1275 return ShellUtil::UNKNOWN_DEFAULT;
1277 if (!base::FilePath::CompareEqualIgnoreCase(short_path, short_app_path))
1278 return ShellUtil::NOT_DEFAULT;
1281 return ShellUtil::IS_DEFAULT;
1284 // A helper function that probes default protocol handler registration (in a
1285 // manner appropriate for the current version of Windows) to determine if
1286 // Chrome is the default handler for |protocols|. Returns IS_DEFAULT
1287 // only if Chrome is the default for all specified protocols.
1288 ShellUtil::DefaultState ProbeProtocolHandlers(
1289 const base::FilePath& chrome_exe,
1290 const wchar_t* const* protocols,
1291 size_t num_protocols) {
1292 #if DCHECK_IS_ON
1293 DCHECK(!num_protocols || protocols);
1294 for (size_t i = 0; i < num_protocols; ++i)
1295 DCHECK(protocols[i] && *protocols[i]);
1296 #endif
1298 const base::win::Version windows_version = base::win::GetVersion();
1300 if (windows_version >= base::win::VERSION_WIN8)
1301 return ProbeCurrentDefaultHandlers(chrome_exe, protocols, num_protocols);
1302 else if (windows_version >= base::win::VERSION_VISTA)
1303 return ProbeAppIsDefaultHandlers(chrome_exe, protocols, num_protocols);
1305 return ProbeOpenCommandHandlers(chrome_exe, protocols, num_protocols);
1308 // (Windows 8+) Finds and stores an app shortcuts folder path in *|path|.
1309 // Returns true on success.
1310 bool GetAppShortcutsFolder(BrowserDistribution* dist,
1311 ShellUtil::ShellChange level,
1312 base::FilePath *path) {
1313 DCHECK(path);
1314 DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8);
1316 base::FilePath folder;
1317 if (!PathService::Get(base::DIR_APP_SHORTCUTS, &folder)) {
1318 LOG(ERROR) << "Could not get application shortcuts location.";
1319 return false;
1322 folder = folder.Append(
1323 ShellUtil::GetBrowserModelId(dist, level == ShellUtil::CURRENT_USER));
1324 if (!base::DirectoryExists(folder)) {
1325 VLOG(1) << "No start screen shortcuts.";
1326 return false;
1329 *path = folder;
1330 return true;
1333 // Shortcut filters for BatchShortcutAction().
1335 typedef base::Callback<bool(const base::FilePath& /*shortcut_path*/,
1336 const base::string16& /*args*/)>
1337 ShortcutFilterCallback;
1339 // FilterTargetEq is a shortcut filter that matches only shortcuts that have a
1340 // specific target, and optionally matches shortcuts that have non-empty
1341 // arguments.
1342 class FilterTargetEq {
1343 public:
1344 FilterTargetEq(const base::FilePath& desired_target_exe, bool require_args);
1346 // Returns true if filter rules are satisfied, i.e.:
1347 // - |target_path|'s target == |desired_target_compare_|, and
1348 // - |args| is non-empty (if |require_args_| == true).
1349 bool Match(const base::FilePath& target_path,
1350 const base::string16& args) const;
1352 // A convenience routine to create a callback to call Match().
1353 // The callback is only valid during the lifetime of the FilterTargetEq
1354 // instance.
1355 ShortcutFilterCallback AsShortcutFilterCallback();
1357 private:
1358 InstallUtil::ProgramCompare desired_target_compare_;
1360 bool require_args_;
1363 FilterTargetEq::FilterTargetEq(const base::FilePath& desired_target_exe,
1364 bool require_args)
1365 : desired_target_compare_(desired_target_exe),
1366 require_args_(require_args) {}
1368 bool FilterTargetEq::Match(const base::FilePath& target_path,
1369 const base::string16& args) const {
1370 if (!desired_target_compare_.EvaluatePath(target_path))
1371 return false;
1372 if (require_args_ && args.empty())
1373 return false;
1374 return true;
1377 ShortcutFilterCallback FilterTargetEq::AsShortcutFilterCallback() {
1378 return base::Bind(&FilterTargetEq::Match, base::Unretained(this));
1381 // Shortcut operations for BatchShortcutAction().
1383 typedef base::Callback<bool(const base::FilePath& /*shortcut_path*/)>
1384 ShortcutOperationCallback;
1386 bool ShortcutOpUnpin(const base::FilePath& shortcut_path) {
1387 VLOG(1) << "Trying to unpin " << shortcut_path.value();
1388 if (!base::win::TaskbarUnpinShortcutLink(shortcut_path.value().c_str())) {
1389 VLOG(1) << shortcut_path.value() << " wasn't pinned (or the unpin failed).";
1390 // No error, since shortcut might not be pinned.
1392 return true;
1395 bool ShortcutOpDelete(const base::FilePath& shortcut_path) {
1396 bool ret = base::DeleteFile(shortcut_path, false);
1397 LOG_IF(ERROR, !ret) << "Failed to remove " << shortcut_path.value();
1398 return ret;
1401 bool ShortcutOpRetarget(const base::FilePath& old_target,
1402 const base::FilePath& new_target,
1403 const base::FilePath& shortcut_path) {
1404 base::win::ShortcutProperties new_prop;
1405 new_prop.set_target(new_target);
1407 // If the old icon matches old target, then update icon while keeping the old
1408 // icon index. Non-fatal if we fail to get the old icon.
1409 base::win::ShortcutProperties old_prop;
1410 if (base::win::ResolveShortcutProperties(
1411 shortcut_path,
1412 base::win::ShortcutProperties::PROPERTIES_ICON,
1413 &old_prop)) {
1414 if (InstallUtil::ProgramCompare(old_target).EvaluatePath(old_prop.icon))
1415 new_prop.set_icon(new_target, old_prop.icon_index);
1416 } else {
1417 LOG(ERROR) << "Failed to resolve " << shortcut_path.value();
1420 bool result = base::win::CreateOrUpdateShortcutLink(
1421 shortcut_path, new_prop, base::win::SHORTCUT_UPDATE_EXISTING);
1422 LOG_IF(ERROR, !result) << "Failed to retarget " << shortcut_path.value();
1423 return result;
1426 bool ShortcutOpListOrRemoveUnknownArgs(
1427 bool do_removal,
1428 std::vector<std::pair<base::FilePath, base::string16> >* shortcuts,
1429 const base::FilePath& shortcut_path) {
1430 base::string16 args;
1431 if (!base::win::ResolveShortcut(shortcut_path, NULL, &args))
1432 return false;
1434 CommandLine current_args(CommandLine::FromString(base::StringPrintf(
1435 L"unused_program %ls", args.c_str())));
1436 const char* const kept_switches[] = {
1437 switches::kApp,
1438 switches::kAppId,
1439 switches::kShowAppList,
1440 switches::kProfileDirectory,
1442 CommandLine desired_args(CommandLine::NO_PROGRAM);
1443 desired_args.CopySwitchesFrom(current_args, kept_switches,
1444 arraysize(kept_switches));
1445 if (desired_args.argv().size() == current_args.argv().size())
1446 return true;
1447 if (shortcuts)
1448 shortcuts->push_back(std::make_pair(shortcut_path, args));
1449 if (!do_removal)
1450 return true;
1451 base::win::ShortcutProperties updated_properties;
1452 updated_properties.set_arguments(desired_args.GetArgumentsString());
1453 return base::win::CreateOrUpdateShortcutLink(
1454 shortcut_path, updated_properties, base::win::SHORTCUT_UPDATE_EXISTING);
1457 // {|location|, |dist|, |level|} determine |shortcut_folder|.
1458 // For each shortcut in |shortcut_folder| that match |shortcut_filter|, apply
1459 // |shortcut_operation|. Returns true if all operations are successful.
1460 // All intended operations are attempted, even if failures occur.
1461 // This method will abort and return false if |cancel| is non-NULL and gets set
1462 // at any point during this call.
1463 bool BatchShortcutAction(
1464 const ShortcutFilterCallback& shortcut_filter,
1465 const ShortcutOperationCallback& shortcut_operation,
1466 ShellUtil::ShortcutLocation location,
1467 BrowserDistribution* dist,
1468 ShellUtil::ShellChange level,
1469 const scoped_refptr<ShellUtil::SharedCancellationFlag>& cancel) {
1470 DCHECK(!shortcut_operation.is_null());
1472 // There is no system-level Quick Launch shortcut folder.
1473 if (level == ShellUtil::SYSTEM_LEVEL &&
1474 location == ShellUtil::SHORTCUT_LOCATION_QUICK_LAUNCH) {
1475 return true;
1478 base::FilePath shortcut_folder;
1479 if (!ShellUtil::GetShortcutPath(location, dist, level, &shortcut_folder)) {
1480 LOG(WARNING) << "Cannot find path at location " << location;
1481 return false;
1484 bool success = true;
1485 base::FileEnumerator enumerator(
1486 shortcut_folder, false, base::FileEnumerator::FILES,
1487 base::string16(L"*") + installer::kLnkExt);
1488 base::FilePath target_path;
1489 base::string16 args;
1490 for (base::FilePath shortcut_path = enumerator.Next();
1491 !shortcut_path.empty();
1492 shortcut_path = enumerator.Next()) {
1493 if (cancel && cancel->data.IsSet())
1494 return false;
1495 if (base::win::ResolveShortcut(shortcut_path, &target_path, &args)) {
1496 if (shortcut_filter.Run(target_path, args) &&
1497 !shortcut_operation.Run(shortcut_path)) {
1498 success = false;
1500 } else {
1501 LOG(ERROR) << "Cannot resolve shortcut at " << shortcut_path.value();
1502 success = false;
1505 return success;
1509 // If the folder specified by {|location|, |dist|, |level|} is empty, remove it.
1510 // Otherwise do nothing. Returns true on success, including the vacuous case
1511 // where no deletion occurred because directory is non-empty.
1512 bool RemoveShortcutFolderIfEmpty(ShellUtil::ShortcutLocation location,
1513 BrowserDistribution* dist,
1514 ShellUtil::ShellChange level) {
1515 // Explicitly whitelist locations, since accidental calls can be very harmful.
1516 if (location != ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR &&
1517 location != ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR &&
1518 location != ShellUtil::SHORTCUT_LOCATION_APP_SHORTCUTS) {
1519 NOTREACHED();
1520 return false;
1523 base::FilePath shortcut_folder;
1524 if (!ShellUtil::GetShortcutPath(location, dist, level, &shortcut_folder)) {
1525 LOG(WARNING) << "Cannot find path at location " << location;
1526 return false;
1528 if (base::IsDirectoryEmpty(shortcut_folder) &&
1529 !base::DeleteFile(shortcut_folder, true)) {
1530 LOG(ERROR) << "Cannot remove folder " << shortcut_folder.value();
1531 return false;
1533 return true;
1536 } // namespace
1538 const wchar_t* ShellUtil::kRegDefaultIcon = L"\\DefaultIcon";
1539 const wchar_t* ShellUtil::kRegShellPath = L"\\shell";
1540 const wchar_t* ShellUtil::kRegShellOpen = L"\\shell\\open\\command";
1541 const wchar_t* ShellUtil::kRegStartMenuInternet =
1542 L"Software\\Clients\\StartMenuInternet";
1543 const wchar_t* ShellUtil::kRegClasses = L"Software\\Classes";
1544 const wchar_t* ShellUtil::kRegRegisteredApplications =
1545 L"Software\\RegisteredApplications";
1546 const wchar_t* ShellUtil::kRegVistaUrlPrefs =
1547 L"Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
1548 L"http\\UserChoice";
1549 const wchar_t* ShellUtil::kAppPathsRegistryKey =
1550 L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths";
1551 const wchar_t* ShellUtil::kAppPathsRegistryPathName = L"Path";
1553 const wchar_t* ShellUtil::kDefaultFileAssociations[] = {L".htm", L".html",
1554 L".shtml", L".xht", L".xhtml", NULL};
1555 const wchar_t* ShellUtil::kPotentialFileAssociations[] = {L".htm", L".html",
1556 L".shtml", L".xht", L".xhtml", L".webp", NULL};
1557 const wchar_t* ShellUtil::kBrowserProtocolAssociations[] = {L"ftp", L"http",
1558 L"https", NULL};
1559 const wchar_t* ShellUtil::kPotentialProtocolAssociations[] = {L"ftp", L"http",
1560 L"https", L"irc", L"mailto", L"mms", L"news", L"nntp", L"sms", L"smsto",
1561 L"tel", L"urn", L"webcal", NULL};
1562 const wchar_t* ShellUtil::kRegUrlProtocol = L"URL Protocol";
1563 const wchar_t* ShellUtil::kRegApplication = L"\\Application";
1564 const wchar_t* ShellUtil::kRegAppUserModelId = L"AppUserModelId";
1565 const wchar_t* ShellUtil::kRegApplicationDescription =
1566 L"ApplicationDescription";
1567 const wchar_t* ShellUtil::kRegApplicationName = L"ApplicationName";
1568 const wchar_t* ShellUtil::kRegApplicationIcon = L"ApplicationIcon";
1569 const wchar_t* ShellUtil::kRegApplicationCompany = L"ApplicationCompany";
1570 const wchar_t* ShellUtil::kRegExePath = L"\\.exe";
1571 const wchar_t* ShellUtil::kRegVerbOpen = L"open";
1572 const wchar_t* ShellUtil::kRegVerbOpenNewWindow = L"opennewwindow";
1573 const wchar_t* ShellUtil::kRegVerbRun = L"run";
1574 const wchar_t* ShellUtil::kRegCommand = L"command";
1575 const wchar_t* ShellUtil::kRegDelegateExecute = L"DelegateExecute";
1576 const wchar_t* ShellUtil::kRegOpenWithProgids = L"OpenWithProgids";
1578 bool ShellUtil::QuickIsChromeRegisteredInHKLM(BrowserDistribution* dist,
1579 const base::string16& chrome_exe,
1580 const base::string16& suffix) {
1581 return QuickIsChromeRegistered(dist, chrome_exe, suffix,
1582 CONFIRM_SHELL_REGISTRATION_IN_HKLM);
1585 bool ShellUtil::ShortcutLocationIsSupported(
1586 ShellUtil::ShortcutLocation location) {
1587 switch (location) {
1588 case SHORTCUT_LOCATION_DESKTOP: // Falls through.
1589 case SHORTCUT_LOCATION_QUICK_LAUNCH: // Falls through.
1590 case SHORTCUT_LOCATION_START_MENU_ROOT: // Falls through.
1591 case SHORTCUT_LOCATION_START_MENU_CHROME_DIR: // Falls through.
1592 case SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR:
1593 return true;
1594 case SHORTCUT_LOCATION_TASKBAR_PINS:
1595 return base::win::GetVersion() >= base::win::VERSION_WIN7;
1596 case SHORTCUT_LOCATION_APP_SHORTCUTS:
1597 return base::win::GetVersion() >= base::win::VERSION_WIN8;
1598 default:
1599 NOTREACHED();
1600 return false;
1604 bool ShellUtil::GetShortcutPath(ShellUtil::ShortcutLocation location,
1605 BrowserDistribution* dist,
1606 ShellChange level,
1607 base::FilePath* path) {
1608 DCHECK(path);
1609 int dir_key = -1;
1610 base::string16 folder_to_append;
1611 switch (location) {
1612 case SHORTCUT_LOCATION_DESKTOP:
1613 dir_key = (level == CURRENT_USER) ? base::DIR_USER_DESKTOP :
1614 base::DIR_COMMON_DESKTOP;
1615 break;
1616 case SHORTCUT_LOCATION_QUICK_LAUNCH:
1617 // There is no support for a system-level Quick Launch shortcut.
1618 DCHECK_EQ(level, CURRENT_USER);
1619 dir_key = base::DIR_USER_QUICK_LAUNCH;
1620 break;
1621 case SHORTCUT_LOCATION_START_MENU_ROOT:
1622 dir_key = (level == CURRENT_USER) ? base::DIR_START_MENU :
1623 base::DIR_COMMON_START_MENU;
1624 break;
1625 case SHORTCUT_LOCATION_START_MENU_CHROME_DIR:
1626 dir_key = (level == CURRENT_USER) ? base::DIR_START_MENU :
1627 base::DIR_COMMON_START_MENU;
1628 folder_to_append = dist->GetStartMenuShortcutSubfolder(
1629 BrowserDistribution::SUBFOLDER_CHROME);
1630 break;
1631 case SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR:
1632 dir_key = (level == CURRENT_USER) ? base::DIR_START_MENU :
1633 base::DIR_COMMON_START_MENU;
1634 folder_to_append = dist->GetStartMenuShortcutSubfolder(
1635 BrowserDistribution::SUBFOLDER_APPS);
1636 break;
1637 case SHORTCUT_LOCATION_TASKBAR_PINS:
1638 dir_key = base::DIR_TASKBAR_PINS;
1639 break;
1640 case SHORTCUT_LOCATION_APP_SHORTCUTS:
1641 // TODO(huangs): Move GetAppShortcutsFolder() logic into base_paths_win.
1642 return GetAppShortcutsFolder(dist, level, path);
1644 default:
1645 NOTREACHED();
1646 return false;
1649 if (!PathService::Get(dir_key, path) || path->empty()) {
1650 NOTREACHED() << dir_key;
1651 return false;
1654 if (!folder_to_append.empty())
1655 *path = path->Append(folder_to_append);
1657 return true;
1660 bool ShellUtil::CreateOrUpdateShortcut(
1661 ShellUtil::ShortcutLocation location,
1662 BrowserDistribution* dist,
1663 const ShellUtil::ShortcutProperties& properties,
1664 ShellUtil::ShortcutOperation operation) {
1665 // Explicitly whitelist locations to which this is applicable.
1666 if (location != SHORTCUT_LOCATION_DESKTOP &&
1667 location != SHORTCUT_LOCATION_QUICK_LAUNCH &&
1668 location != SHORTCUT_LOCATION_START_MENU_ROOT &&
1669 location != SHORTCUT_LOCATION_START_MENU_CHROME_DIR &&
1670 location != SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR) {
1671 NOTREACHED();
1672 return false;
1675 DCHECK(dist);
1676 // |pin_to_taskbar| is only acknowledged when first creating the shortcut.
1677 DCHECK(!properties.pin_to_taskbar ||
1678 operation == SHELL_SHORTCUT_CREATE_ALWAYS ||
1679 operation == SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL);
1681 base::FilePath user_shortcut_path;
1682 base::FilePath system_shortcut_path;
1683 if (location == SHORTCUT_LOCATION_QUICK_LAUNCH) {
1684 // There is no system-level shortcut for Quick Launch.
1685 DCHECK_EQ(properties.level, CURRENT_USER);
1686 } else if (!GetShortcutPath(
1687 location, dist, SYSTEM_LEVEL, &system_shortcut_path)) {
1688 NOTREACHED();
1689 return false;
1692 base::string16 shortcut_name(
1693 ExtractShortcutNameFromProperties(dist, properties));
1694 system_shortcut_path = system_shortcut_path.Append(shortcut_name);
1696 base::FilePath* chosen_path;
1697 bool should_install_shortcut = true;
1698 if (properties.level == SYSTEM_LEVEL) {
1699 // Install the system-level shortcut if requested.
1700 chosen_path = &system_shortcut_path;
1701 } else if (operation != SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL ||
1702 system_shortcut_path.empty() ||
1703 !base::PathExists(system_shortcut_path)) {
1704 // Otherwise install the user-level shortcut, unless the system-level
1705 // variant of this shortcut is present on the machine and |operation| states
1706 // not to create a user-level shortcut in that case.
1707 if (!GetShortcutPath(location, dist, CURRENT_USER, &user_shortcut_path)) {
1708 NOTREACHED();
1709 return false;
1711 user_shortcut_path = user_shortcut_path.Append(shortcut_name);
1712 chosen_path = &user_shortcut_path;
1713 } else {
1714 // Do not install any shortcut if we are told to install a user-level
1715 // shortcut, but the system-level variant of that shortcut is present.
1716 // Other actions (e.g., pinning) can still happen with respect to the
1717 // existing system-level shortcut however.
1718 chosen_path = &system_shortcut_path;
1719 should_install_shortcut = false;
1722 if (chosen_path == NULL || chosen_path->empty()) {
1723 NOTREACHED();
1724 return false;
1727 base::win::ShortcutOperation shortcut_operation =
1728 TranslateShortcutOperation(operation);
1729 bool ret = true;
1730 if (should_install_shortcut) {
1731 // Make sure the parent directories exist when creating the shortcut.
1732 if (shortcut_operation == base::win::SHORTCUT_CREATE_ALWAYS &&
1733 !base::CreateDirectory(chosen_path->DirName())) {
1734 NOTREACHED();
1735 return false;
1738 base::win::ShortcutProperties shortcut_properties(
1739 TranslateShortcutProperties(properties));
1740 ret = base::win::CreateOrUpdateShortcutLink(
1741 *chosen_path, shortcut_properties, shortcut_operation);
1744 if (ret && shortcut_operation == base::win::SHORTCUT_CREATE_ALWAYS &&
1745 properties.pin_to_taskbar &&
1746 base::win::GetVersion() >= base::win::VERSION_WIN7) {
1747 ret = base::win::TaskbarPinShortcutLink(chosen_path->value().c_str());
1748 if (!ret) {
1749 LOG(ERROR) << "Failed to pin " << chosen_path->value();
1753 return ret;
1756 base::string16 ShellUtil::FormatIconLocation(const base::string16& icon_path,
1757 int icon_index) {
1758 base::string16 icon_string(icon_path);
1759 icon_string.append(L",");
1760 icon_string.append(base::IntToString16(icon_index));
1761 return icon_string;
1764 base::string16 ShellUtil::GetChromeShellOpenCmd(
1765 const base::string16& chrome_exe) {
1766 return L"\"" + chrome_exe + L"\" -- \"%1\"";
1769 base::string16 ShellUtil::GetChromeDelegateCommand(
1770 const base::string16& chrome_exe) {
1771 return L"\"" + chrome_exe + L"\" -- %*";
1774 void ShellUtil::GetRegisteredBrowsers(
1775 BrowserDistribution* dist,
1776 std::map<base::string16, base::string16>* browsers) {
1777 DCHECK(dist);
1778 DCHECK(browsers);
1780 const base::string16 base_key(ShellUtil::kRegStartMenuInternet);
1781 base::string16 client_path;
1782 RegKey key;
1783 base::string16 name;
1784 base::string16 command;
1786 // HKCU has precedence over HKLM for these registrations: http://goo.gl/xjczJ.
1787 // Look in HKCU second to override any identical values found in HKLM.
1788 const HKEY roots[] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER };
1789 for (int i = 0; i < arraysize(roots); ++i) {
1790 const HKEY root = roots[i];
1791 for (base::win::RegistryKeyIterator iter(root, base_key.c_str());
1792 iter.Valid(); ++iter) {
1793 client_path.assign(base_key).append(1, L'\\').append(iter.Name());
1794 // Read the browser's name (localized according to install language).
1795 if (key.Open(root, client_path.c_str(),
1796 KEY_QUERY_VALUE) != ERROR_SUCCESS ||
1797 key.ReadValue(NULL, &name) != ERROR_SUCCESS ||
1798 name.empty() ||
1799 name.find(dist->GetBaseAppName()) != base::string16::npos) {
1800 continue;
1802 // Read the browser's reinstall command.
1803 if (key.Open(root, (client_path + L"\\InstallInfo").c_str(),
1804 KEY_QUERY_VALUE) == ERROR_SUCCESS &&
1805 key.ReadValue(kReinstallCommand, &command) == ERROR_SUCCESS &&
1806 !command.empty()) {
1807 (*browsers)[name] = command;
1813 base::string16 ShellUtil::GetCurrentInstallationSuffix(
1814 BrowserDistribution* dist,
1815 const base::string16& chrome_exe) {
1816 // This method is somewhat the opposite of GetInstallationSpecificSuffix().
1817 // In this case we are not trying to determine the current suffix for the
1818 // upcoming installation (i.e. not trying to stick to a currently bad
1819 // registration style if one is present).
1820 // Here we want to determine which suffix we should use at run-time.
1821 // In order of preference, we prefer (for user-level installs):
1822 // 1) Base 32 encoding of the md5 hash of the user's sid (new-style).
1823 // 2) Username (old-style).
1824 // 3) Unsuffixed (even worse).
1825 base::string16 tested_suffix;
1826 if (InstallUtil::IsPerUserInstall(chrome_exe.c_str()) &&
1827 (!GetUserSpecificRegistrySuffix(&tested_suffix) ||
1828 !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix,
1829 CONFIRM_PROGID_REGISTRATION)) &&
1830 (!GetOldUserSpecificRegistrySuffix(&tested_suffix) ||
1831 !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix,
1832 CONFIRM_PROGID_REGISTRATION)) &&
1833 !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix.erase(),
1834 CONFIRM_PROGID_REGISTRATION)) {
1835 // If Chrome is not registered under any of the possible suffixes (e.g.
1836 // tests, Canary, etc.): use the new-style suffix at run-time.
1837 if (!GetUserSpecificRegistrySuffix(&tested_suffix))
1838 NOTREACHED();
1840 return tested_suffix;
1843 base::string16 ShellUtil::GetApplicationName(BrowserDistribution* dist,
1844 const base::string16& chrome_exe) {
1845 base::string16 app_name = dist->GetBaseAppName();
1846 app_name += GetCurrentInstallationSuffix(dist, chrome_exe);
1847 return app_name;
1850 base::string16 ShellUtil::GetBrowserModelId(BrowserDistribution* dist,
1851 bool is_per_user_install) {
1852 base::string16 app_id(dist->GetBaseAppId());
1853 base::string16 suffix;
1855 // TODO(robertshield): Temporary hack to make the kRegisterChromeBrowserSuffix
1856 // apply to all registry values computed down in these murky depths.
1857 CommandLine& command_line = *CommandLine::ForCurrentProcess();
1858 if (command_line.HasSwitch(
1859 installer::switches::kRegisterChromeBrowserSuffix)) {
1860 suffix = command_line.GetSwitchValueNative(
1861 installer::switches::kRegisterChromeBrowserSuffix);
1862 } else if (is_per_user_install && !GetUserSpecificRegistrySuffix(&suffix)) {
1863 NOTREACHED();
1865 // There is only one component (i.e. the suffixed appid) in this case, but it
1866 // is still necessary to go through the appid constructor to make sure the
1867 // returned appid is truncated if necessary.
1868 std::vector<base::string16> components(1, app_id.append(suffix));
1869 return BuildAppModelId(components);
1872 base::string16 ShellUtil::BuildAppModelId(
1873 const std::vector<base::string16>& components) {
1874 DCHECK_GT(components.size(), 0U);
1876 // Find the maximum numbers of characters allowed in each component
1877 // (accounting for the dots added between each component).
1878 const size_t available_chars =
1879 installer::kMaxAppModelIdLength - (components.size() - 1);
1880 const size_t max_component_length = available_chars / components.size();
1882 // |max_component_length| should be at least 2; otherwise the truncation logic
1883 // below breaks.
1884 if (max_component_length < 2U) {
1885 NOTREACHED();
1886 return (*components.begin()).substr(0, installer::kMaxAppModelIdLength);
1889 base::string16 app_id;
1890 app_id.reserve(installer::kMaxAppModelIdLength);
1891 for (std::vector<base::string16>::const_iterator it = components.begin();
1892 it != components.end(); ++it) {
1893 if (it != components.begin())
1894 app_id.push_back(L'.');
1896 const base::string16& component = *it;
1897 DCHECK(!component.empty());
1898 if (component.length() > max_component_length) {
1899 // Append a shortened version of this component. Cut in the middle to try
1900 // to avoid losing the unique parts of this component (which are usually
1901 // at the beginning or end for things like usernames and paths).
1902 app_id.append(component.c_str(), 0, max_component_length / 2);
1903 app_id.append(component.c_str(),
1904 component.length() - ((max_component_length + 1) / 2),
1905 base::string16::npos);
1906 } else {
1907 app_id.append(component);
1910 // No spaces are allowed in the AppUserModelId according to MSDN.
1911 base::ReplaceChars(app_id, base::ASCIIToUTF16(" "), base::ASCIIToUTF16("_"),
1912 &app_id);
1913 return app_id;
1916 ShellUtil::DefaultState ShellUtil::GetChromeDefaultState() {
1917 base::FilePath app_path;
1918 if (!PathService::Get(base::FILE_EXE, &app_path)) {
1919 NOTREACHED();
1920 return ShellUtil::UNKNOWN_DEFAULT;
1923 return GetChromeDefaultStateFromPath(app_path);
1926 ShellUtil::DefaultState ShellUtil::GetChromeDefaultStateFromPath(
1927 const base::FilePath& chrome_exe) {
1928 BrowserDistribution* distribution = BrowserDistribution::GetDistribution();
1929 if (distribution->GetDefaultBrowserControlPolicy() ==
1930 BrowserDistribution::DEFAULT_BROWSER_UNSUPPORTED) {
1931 return NOT_DEFAULT;
1933 // When we check for default browser we don't necessarily want to count file
1934 // type handlers and icons as having changed the default browser status,
1935 // since the user may have changed their shell settings to cause HTML files
1936 // to open with a text editor for example. We also don't want to aggressively
1937 // claim FTP, since the user may have a separate FTP client. It is an open
1938 // question as to how to "heal" these settings. Perhaps the user should just
1939 // re-run the installer or run with the --set-default-browser command line
1940 // flag. There is doubtless some other key we can hook into to cause "Repair"
1941 // to show up in Add/Remove programs for us.
1942 static const wchar_t* const kChromeProtocols[] = { L"http", L"https" };
1943 return ProbeProtocolHandlers(chrome_exe,
1944 kChromeProtocols,
1945 arraysize(kChromeProtocols));
1948 ShellUtil::DefaultState ShellUtil::GetChromeDefaultProtocolClientState(
1949 const base::string16& protocol) {
1950 BrowserDistribution* distribution = BrowserDistribution::GetDistribution();
1951 if (distribution->GetDefaultBrowserControlPolicy() ==
1952 BrowserDistribution::DEFAULT_BROWSER_UNSUPPORTED) {
1953 return NOT_DEFAULT;
1956 if (protocol.empty())
1957 return UNKNOWN_DEFAULT;
1959 base::FilePath chrome_exe;
1960 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
1961 NOTREACHED();
1962 return ShellUtil::UNKNOWN_DEFAULT;
1965 const wchar_t* const protocols[] = { protocol.c_str() };
1966 return ProbeProtocolHandlers(chrome_exe,
1967 protocols,
1968 arraysize(protocols));
1971 // static
1972 bool ShellUtil::CanMakeChromeDefaultUnattended() {
1973 return base::win::GetVersion() < base::win::VERSION_WIN8;
1976 bool ShellUtil::MakeChromeDefault(BrowserDistribution* dist,
1977 int shell_change,
1978 const base::string16& chrome_exe,
1979 bool elevate_if_not_admin) {
1980 DCHECK(!(shell_change & ShellUtil::SYSTEM_LEVEL) || IsUserAnAdmin());
1982 BrowserDistribution* distribution = BrowserDistribution::GetDistribution();
1983 if (distribution->GetDefaultBrowserControlPolicy() !=
1984 BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL) {
1985 return false;
1988 // Windows 8 does not permit making a browser default just like that.
1989 // This process needs to be routed through the system's UI. Use
1990 // ShowMakeChromeDefaultSystemUI instead (below).
1991 if (!CanMakeChromeDefaultUnattended()) {
1992 return false;
1995 if (!ShellUtil::RegisterChromeBrowser(
1996 dist, chrome_exe, base::string16(), elevate_if_not_admin)) {
1997 return false;
2000 bool ret = true;
2001 // First use the new "recommended" way on Vista to make Chrome default
2002 // browser.
2003 base::string16 app_name = GetApplicationName(dist, chrome_exe);
2005 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
2006 // On Windows Vista and Win7 we still can set ourselves via the
2007 // the IApplicationAssociationRegistration interface.
2008 VLOG(1) << "Registering Chrome as default browser on Vista.";
2009 base::win::ScopedComPtr<IApplicationAssociationRegistration> pAAR;
2010 HRESULT hr = pAAR.CreateInstance(CLSID_ApplicationAssociationRegistration,
2011 NULL, CLSCTX_INPROC);
2012 if (SUCCEEDED(hr)) {
2013 for (int i = 0; ShellUtil::kBrowserProtocolAssociations[i] != NULL; i++) {
2014 hr = pAAR->SetAppAsDefault(app_name.c_str(),
2015 ShellUtil::kBrowserProtocolAssociations[i], AT_URLPROTOCOL);
2016 if (!SUCCEEDED(hr)) {
2017 ret = false;
2018 LOG(ERROR) << "Failed to register as default for protocol "
2019 << ShellUtil::kBrowserProtocolAssociations[i]
2020 << " (" << hr << ")";
2024 for (int i = 0; ShellUtil::kDefaultFileAssociations[i] != NULL; i++) {
2025 hr = pAAR->SetAppAsDefault(app_name.c_str(),
2026 ShellUtil::kDefaultFileAssociations[i], AT_FILEEXTENSION);
2027 if (!SUCCEEDED(hr)) {
2028 ret = false;
2029 LOG(ERROR) << "Failed to register as default for file extension "
2030 << ShellUtil::kDefaultFileAssociations[i]
2031 << " (" << hr << ")";
2037 if (!RegisterChromeAsDefaultXPStyle(dist, shell_change, chrome_exe))
2038 ret = false;
2040 // Send Windows notification event so that it can update icons for
2041 // file associations.
2042 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
2043 return ret;
2046 bool ShellUtil::ShowMakeChromeDefaultSystemUI(
2047 BrowserDistribution* dist,
2048 const base::string16& chrome_exe) {
2049 DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8);
2050 if (dist->GetDefaultBrowserControlPolicy() !=
2051 BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL) {
2052 return false;
2055 if (!RegisterChromeBrowser(dist, chrome_exe, base::string16(), true))
2056 return false;
2058 bool succeeded = true;
2059 bool is_default = (GetChromeDefaultState() == IS_DEFAULT);
2060 if (!is_default) {
2061 // On Windows 8, you can't set yourself as the default handler
2062 // programatically. In other words IApplicationAssociationRegistration
2063 // has been rendered useless. What you can do is to launch
2064 // "Set Program Associations" section of the "Default Programs"
2065 // control panel, which is a mess, or pop the concise "How you want to open
2066 // webpages?" dialog. We choose the latter.
2067 succeeded = LaunchSelectDefaultProtocolHandlerDialog(L"http");
2068 is_default = (succeeded && GetChromeDefaultState() == IS_DEFAULT);
2070 if (succeeded && is_default)
2071 RegisterChromeAsDefaultXPStyle(dist, CURRENT_USER, chrome_exe);
2072 return succeeded;
2075 bool ShellUtil::MakeChromeDefaultProtocolClient(
2076 BrowserDistribution* dist,
2077 const base::string16& chrome_exe,
2078 const base::string16& protocol) {
2079 if (dist->GetDefaultBrowserControlPolicy() !=
2080 BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL) {
2081 return false;
2084 if (!RegisterChromeForProtocol(
2085 dist, chrome_exe, base::string16(), protocol, true))
2086 return false;
2088 // Windows 8 does not permit making a browser default just like that.
2089 // This process needs to be routed through the system's UI. Use
2090 // ShowMakeChromeDefaultProocolClientSystemUI instead (below).
2091 if (!CanMakeChromeDefaultUnattended())
2092 return false;
2094 bool ret = true;
2095 // First use the new "recommended" way on Vista to make Chrome default
2096 // protocol handler.
2097 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
2098 VLOG(1) << "Registering Chrome as default handler for " << protocol
2099 << " on Vista.";
2100 base::win::ScopedComPtr<IApplicationAssociationRegistration> pAAR;
2101 HRESULT hr = pAAR.CreateInstance(CLSID_ApplicationAssociationRegistration,
2102 NULL, CLSCTX_INPROC);
2103 if (SUCCEEDED(hr)) {
2104 base::string16 app_name = GetApplicationName(dist, chrome_exe);
2105 hr = pAAR->SetAppAsDefault(app_name.c_str(), protocol.c_str(),
2106 AT_URLPROTOCOL);
2108 if (!SUCCEEDED(hr)) {
2109 ret = false;
2110 LOG(ERROR) << "Could not make Chrome default protocol client (Vista):"
2111 << " HRESULT=" << hr << ".";
2115 // Now use the old way to associate Chrome with the desired protocol. This
2116 // should not be required on Vista+, but since some applications still read
2117 // Software\Classes\<protocol> key directly, do this on Vista+ also.
2118 if (!RegisterChromeAsDefaultProtocolClientXPStyle(dist, chrome_exe, protocol))
2119 ret = false;
2121 return ret;
2124 bool ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(
2125 BrowserDistribution* dist,
2126 const base::string16& chrome_exe,
2127 const base::string16& protocol) {
2128 DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8);
2129 if (dist->GetDefaultBrowserControlPolicy() !=
2130 BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL) {
2131 return false;
2134 if (!RegisterChromeForProtocol(
2135 dist, chrome_exe, base::string16(), protocol, true))
2136 return false;
2138 bool succeeded = true;
2139 bool is_default = (
2140 GetChromeDefaultProtocolClientState(protocol) == IS_DEFAULT);
2141 if (!is_default) {
2142 // On Windows 8, you can't set yourself as the default handler
2143 // programatically. In other words IApplicationAssociationRegistration
2144 // has been rendered useless. What you can do is to launch
2145 // "Set Program Associations" section of the "Default Programs"
2146 // control panel, which is a mess, or pop the concise "How you want to open
2147 // links of this type (protocol)?" dialog. We choose the latter.
2148 succeeded = LaunchSelectDefaultProtocolHandlerDialog(protocol.c_str());
2149 is_default = (succeeded &&
2150 GetChromeDefaultProtocolClientState(protocol) == IS_DEFAULT);
2152 if (succeeded && is_default)
2153 RegisterChromeAsDefaultProtocolClientXPStyle(dist, chrome_exe, protocol);
2154 return succeeded;
2157 bool ShellUtil::RegisterChromeBrowser(BrowserDistribution* dist,
2158 const base::string16& chrome_exe,
2159 const base::string16& unique_suffix,
2160 bool elevate_if_not_admin) {
2161 if (dist->GetDefaultBrowserControlPolicy() ==
2162 BrowserDistribution::DEFAULT_BROWSER_UNSUPPORTED) {
2163 return false;
2166 CommandLine& command_line = *CommandLine::ForCurrentProcess();
2168 base::string16 suffix;
2169 if (!unique_suffix.empty()) {
2170 suffix = unique_suffix;
2171 } else if (command_line.HasSwitch(
2172 installer::switches::kRegisterChromeBrowserSuffix)) {
2173 suffix = command_line.GetSwitchValueNative(
2174 installer::switches::kRegisterChromeBrowserSuffix);
2175 } else if (!GetInstallationSpecificSuffix(dist, chrome_exe, &suffix)) {
2176 return false;
2179 RemoveRunVerbOnWindows8(dist, chrome_exe);
2181 bool user_level = InstallUtil::IsPerUserInstall(chrome_exe.c_str());
2182 HKEY root = DetermineRegistrationRoot(user_level);
2184 // Look only in HKLM for system-level installs (otherwise, if a user-level
2185 // install is also present, it will lead IsChromeRegistered() to think this
2186 // system-level install isn't registered properly as it is shadowed by the
2187 // user-level install's registrations).
2188 uint32 look_for_in = user_level ?
2189 RegistryEntry::LOOK_IN_HKCU_THEN_HKLM : RegistryEntry::LOOK_IN_HKLM;
2191 // Check if chrome is already registered with this suffix.
2192 if (IsChromeRegistered(dist, chrome_exe, suffix, look_for_in))
2193 return true;
2195 bool result = true;
2196 if (root == HKEY_CURRENT_USER || IsUserAnAdmin()) {
2197 // Do the full registration if we can do it at user-level or if the user is
2198 // an admin.
2199 ScopedVector<RegistryEntry> progid_and_appreg_entries;
2200 ScopedVector<RegistryEntry> shell_entries;
2201 RegistryEntry::GetChromeProgIdEntries(
2202 dist, chrome_exe, suffix, &progid_and_appreg_entries);
2203 RegistryEntry::GetChromeAppRegistrationEntries(
2204 chrome_exe, suffix, &progid_and_appreg_entries);
2205 RegistryEntry::GetShellIntegrationEntries(
2206 dist, chrome_exe, suffix, &shell_entries);
2207 result = (AddRegistryEntries(root, progid_and_appreg_entries) &&
2208 AddRegistryEntries(root, shell_entries));
2209 } else if (elevate_if_not_admin &&
2210 base::win::GetVersion() >= base::win::VERSION_VISTA &&
2211 ElevateAndRegisterChrome(dist, chrome_exe, suffix, base::string16())) {
2212 // If the user is not an admin and OS is between Vista and Windows 7
2213 // inclusively, try to elevate and register. This is only intended for
2214 // user-level installs as system-level installs should always be run with
2215 // admin rights.
2216 result = true;
2217 } else {
2218 // If we got to this point then all we can do is create ProgId and basic app
2219 // registrations under HKCU.
2220 ScopedVector<RegistryEntry> entries;
2221 RegistryEntry::GetChromeProgIdEntries(
2222 dist, chrome_exe, base::string16(), &entries);
2223 // Prefer to use |suffix|; unless Chrome's ProgIds are already registered
2224 // with no suffix (as per the old registration style): in which case some
2225 // other registry entries could refer to them and since we were not able to
2226 // set our HKLM entries above, we are better off not altering these here.
2227 if (!AreEntriesRegistered(entries, RegistryEntry::LOOK_IN_HKCU)) {
2228 if (!suffix.empty()) {
2229 entries.clear();
2230 RegistryEntry::GetChromeProgIdEntries(
2231 dist, chrome_exe, suffix, &entries);
2232 RegistryEntry::GetChromeAppRegistrationEntries(
2233 chrome_exe, suffix, &entries);
2235 result = AddRegistryEntries(HKEY_CURRENT_USER, entries);
2236 } else {
2237 // The ProgId is registered unsuffixed in HKCU, also register the app with
2238 // Windows in HKCU (this was not done in the old registration style and
2239 // thus needs to be done after the above check for the unsuffixed
2240 // registration).
2241 entries.clear();
2242 RegistryEntry::GetChromeAppRegistrationEntries(
2243 chrome_exe, base::string16(), &entries);
2244 result = AddRegistryEntries(HKEY_CURRENT_USER, entries);
2247 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
2248 return result;
2251 bool ShellUtil::RegisterChromeForProtocol(BrowserDistribution* dist,
2252 const base::string16& chrome_exe,
2253 const base::string16& unique_suffix,
2254 const base::string16& protocol,
2255 bool elevate_if_not_admin) {
2256 if (dist->GetDefaultBrowserControlPolicy() ==
2257 BrowserDistribution::DEFAULT_BROWSER_UNSUPPORTED) {
2258 return false;
2261 base::string16 suffix;
2262 if (!unique_suffix.empty()) {
2263 suffix = unique_suffix;
2264 } else if (!GetInstallationSpecificSuffix(dist, chrome_exe, &suffix)) {
2265 return false;
2268 bool user_level = InstallUtil::IsPerUserInstall(chrome_exe.c_str());
2269 HKEY root = DetermineRegistrationRoot(user_level);
2271 // Look only in HKLM for system-level installs (otherwise, if a user-level
2272 // install is also present, it could lead IsChromeRegisteredForProtocol() to
2273 // think this system-level install isn't registered properly as it may be
2274 // shadowed by the user-level install's registrations).
2275 uint32 look_for_in = user_level ?
2276 RegistryEntry::LOOK_IN_HKCU_THEN_HKLM : RegistryEntry::LOOK_IN_HKLM;
2278 // Check if chrome is already registered with this suffix.
2279 if (IsChromeRegisteredForProtocol(dist, suffix, protocol, look_for_in))
2280 return true;
2282 if (root == HKEY_CURRENT_USER || IsUserAnAdmin()) {
2283 // We can do this operation directly.
2284 // First, make sure Chrome is fully registered on this machine.
2285 if (!RegisterChromeBrowser(dist, chrome_exe, suffix, false))
2286 return false;
2288 // Write in the capabillity for the protocol.
2289 ScopedVector<RegistryEntry> entries;
2290 RegistryEntry::GetProtocolCapabilityEntries(dist, suffix, protocol,
2291 &entries);
2292 return AddRegistryEntries(root, entries);
2293 } else if (elevate_if_not_admin &&
2294 base::win::GetVersion() >= base::win::VERSION_VISTA) {
2295 // Elevate to do the whole job
2296 return ElevateAndRegisterChrome(dist, chrome_exe, suffix, protocol);
2297 } else {
2298 // Admin rights are required to register capabilities before Windows 8.
2299 return false;
2303 // static
2304 bool ShellUtil::RemoveShortcuts(ShellUtil::ShortcutLocation location,
2305 BrowserDistribution* dist,
2306 ShellChange level,
2307 const base::FilePath& target_exe) {
2308 if (!ShellUtil::ShortcutLocationIsSupported(location))
2309 return true; // Vacuous success.
2311 FilterTargetEq shortcut_filter(target_exe, false);
2312 // Main operation to apply to each shortcut in the directory specified.
2313 ShortcutOperationCallback shortcut_operation(
2314 location == SHORTCUT_LOCATION_TASKBAR_PINS ?
2315 base::Bind(&ShortcutOpUnpin) : base::Bind(&ShortcutOpDelete));
2316 bool success = BatchShortcutAction(shortcut_filter.AsShortcutFilterCallback(),
2317 shortcut_operation, location, dist, level,
2318 NULL);
2319 // Remove chrome-specific shortcut folders if they are now empty.
2320 if (success &&
2321 (location == SHORTCUT_LOCATION_START_MENU_CHROME_DIR ||
2322 location == SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR ||
2323 location == SHORTCUT_LOCATION_APP_SHORTCUTS)) {
2324 success = RemoveShortcutFolderIfEmpty(location, dist, level);
2326 return success;
2329 // static
2330 bool ShellUtil::RetargetShortcutsWithArgs(
2331 ShellUtil::ShortcutLocation location,
2332 BrowserDistribution* dist,
2333 ShellChange level,
2334 const base::FilePath& old_target_exe,
2335 const base::FilePath& new_target_exe) {
2336 if (!ShellUtil::ShortcutLocationIsSupported(location))
2337 return true; // Vacuous success.
2339 FilterTargetEq shortcut_filter(old_target_exe, true);
2340 ShortcutOperationCallback shortcut_operation(
2341 base::Bind(&ShortcutOpRetarget, old_target_exe, new_target_exe));
2342 return BatchShortcutAction(shortcut_filter.AsShortcutFilterCallback(),
2343 shortcut_operation, location, dist, level, NULL);
2346 // static
2347 bool ShellUtil::ShortcutListMaybeRemoveUnknownArgs(
2348 ShellUtil::ShortcutLocation location,
2349 BrowserDistribution* dist,
2350 ShellChange level,
2351 const base::FilePath& chrome_exe,
2352 bool do_removal,
2353 const scoped_refptr<SharedCancellationFlag>& cancel,
2354 std::vector<std::pair<base::FilePath, base::string16> >* shortcuts) {
2355 if (!ShellUtil::ShortcutLocationIsSupported(location))
2356 return false;
2357 DCHECK(dist);
2358 FilterTargetEq shortcut_filter(chrome_exe, true);
2359 ShortcutOperationCallback shortcut_operation(
2360 base::Bind(&ShortcutOpListOrRemoveUnknownArgs, do_removal, shortcuts));
2361 return BatchShortcutAction(shortcut_filter.AsShortcutFilterCallback(),
2362 shortcut_operation, location, dist, level, cancel);
2365 bool ShellUtil::GetUserSpecificRegistrySuffix(base::string16* suffix) {
2366 // Use a thread-safe cache for the user's suffix.
2367 static base::LazyInstance<UserSpecificRegistrySuffix>::Leaky suffix_instance =
2368 LAZY_INSTANCE_INITIALIZER;
2369 return suffix_instance.Get().GetSuffix(suffix);
2372 bool ShellUtil::GetOldUserSpecificRegistrySuffix(base::string16* suffix) {
2373 wchar_t user_name[256];
2374 DWORD size = arraysize(user_name);
2375 if (::GetUserName(user_name, &size) == 0 || size < 1) {
2376 NOTREACHED();
2377 return false;
2379 suffix->reserve(size);
2380 suffix->assign(1, L'.');
2381 suffix->append(user_name, size - 1);
2382 return true;
2385 base::string16 ShellUtil::ByteArrayToBase32(const uint8* bytes, size_t size) {
2386 static const char kEncoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
2388 // Eliminate special cases first.
2389 if (size == 0) {
2390 return base::string16();
2391 } else if (size == 1) {
2392 base::string16 ret;
2393 ret.push_back(kEncoding[(bytes[0] & 0xf8) >> 3]);
2394 ret.push_back(kEncoding[(bytes[0] & 0x07) << 2]);
2395 return ret;
2396 } else if (size >= std::numeric_limits<size_t>::max() / 8) {
2397 // If |size| is too big, the calculation of |encoded_length| below will
2398 // overflow.
2399 NOTREACHED();
2400 return base::string16();
2403 // Overestimate the number of bits in the string by 4 so that dividing by 5
2404 // is the equivalent of rounding up the actual number of bits divided by 5.
2405 const size_t encoded_length = (size * 8 + 4) / 5;
2407 base::string16 ret;
2408 ret.reserve(encoded_length);
2410 // A bit stream which will be read from the left and appended to from the
2411 // right as it's emptied.
2412 uint16 bit_stream = (bytes[0] << 8) + bytes[1];
2413 size_t next_byte_index = 2;
2414 int free_bits = 0;
2415 while (free_bits < 16) {
2416 // Extract the 5 leftmost bits in the stream
2417 ret.push_back(kEncoding[(bit_stream & 0xf800) >> 11]);
2418 bit_stream <<= 5;
2419 free_bits += 5;
2421 // If there is enough room in the bit stream, inject another byte (if there
2422 // are any left...).
2423 if (free_bits >= 8 && next_byte_index < size) {
2424 free_bits -= 8;
2425 bit_stream += bytes[next_byte_index++] << free_bits;
2429 DCHECK_EQ(ret.length(), encoded_length);
2430 return ret;
2433 // static
2434 bool ShellUtil::AddFileAssociations(
2435 const base::string16& prog_id,
2436 const base::CommandLine& command_line,
2437 const base::string16& file_type_name,
2438 const base::FilePath& icon_path,
2439 const std::set<base::string16>& file_extensions) {
2440 ScopedVector<RegistryEntry> entries;
2442 // Create a class for this app.
2443 RegistryEntry::ApplicationInfo app_info;
2444 app_info.prog_id = prog_id;
2445 app_info.file_type_name = file_type_name;
2446 app_info.file_type_icon_path = icon_path.value();
2447 app_info.file_type_icon_index = 0;
2448 app_info.command_line = command_line.GetCommandLineStringWithPlaceholders();
2449 RegistryEntry::GetProgIdEntries(app_info, &entries);
2451 // Associate each extension that the app can handle with the class. Set this
2452 // app as the default handler if and only if there is no existing default.
2453 for (std::set<base::string16>::const_iterator it = file_extensions.begin();
2454 it != file_extensions.end();
2455 ++it) {
2456 // Do not allow empty file extensions, or extensions beginning with a '.'.
2457 DCHECK(!it->empty());
2458 DCHECK_NE(L'.', (*it)[0]);
2459 base::string16 ext(1, L'.');
2460 ext.append(*it);
2461 RegistryEntry::GetAppExtRegistrationEntries(prog_id, ext, &entries);
2463 // Regstering as the default will have no effect on Windows 8 (see
2464 // documentation for GetAppDefaultRegistrationEntries). However, if our app
2465 // is the only handler, it will automatically become the default, so the
2466 // same effect is achieved.
2467 RegistryEntry::GetAppDefaultRegistrationEntries(
2468 prog_id, ext, false, &entries);
2471 return AddRegistryEntries(HKEY_CURRENT_USER, entries);
2474 // static
2475 bool ShellUtil::DeleteFileAssociations(const base::string16& prog_id) {
2476 // Delete the key HKEY_CURRENT_USER\Software\Classes\PROGID.
2477 base::string16 key_path(ShellUtil::kRegClasses);
2478 key_path.push_back(base::FilePath::kSeparators[0]);
2479 key_path.append(prog_id);
2480 return InstallUtil::DeleteRegistryKey(
2481 HKEY_CURRENT_USER, key_path, WorkItem::kWow64Default);
2483 // TODO(mgiuca): Remove the extension association entries. This requires that
2484 // the extensions associated with a particular prog_id are stored in that
2485 // prog_id's key.