[sql] Remove _HAS_EXCEPTIONS=0 from build info.
[chromium-blink-merge.git] / chrome / installer / util / shell_util.cc
blob46defa119135f89d76b0d7b2578fecf14bdeea31
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>
14 #include <shobjidl.h>
16 #include <limits>
17 #include <string>
19 #include "base/bind.h"
20 #include "base/command_line.h"
21 #include "base/files/file_enumerator.h"
22 #include "base/files/file_path.h"
23 #include "base/files/file_util.h"
24 #include "base/lazy_instance.h"
25 #include "base/logging.h"
26 #include "base/md5.h"
27 #include "base/memory/scoped_ptr.h"
28 #include "base/memory/scoped_vector.h"
29 #include "base/path_service.h"
30 #include "base/strings/string16.h"
31 #include "base/strings/string_number_conversions.h"
32 #include "base/strings/string_split.h"
33 #include "base/strings/string_util.h"
34 #include "base/strings/stringprintf.h"
35 #include "base/strings/utf_string_conversions.h"
36 #include "base/synchronization/cancellation_flag.h"
37 #include "base/values.h"
38 #include "base/win/registry.h"
39 #include "base/win/scoped_co_mem.h"
40 #include "base/win/scoped_comptr.h"
41 #include "base/win/shortcut.h"
42 #include "base/win/win_util.h"
43 #include "base/win/windows_version.h"
44 #include "chrome/common/chrome_constants.h"
45 #include "chrome/common/chrome_switches.h"
46 #include "chrome/installer/util/beacons.h"
47 #include "chrome/installer/util/browser_distribution.h"
48 #include "chrome/installer/util/install_util.h"
49 #include "chrome/installer/util/installer_util_strings.h"
50 #include "chrome/installer/util/l10n_string_util.h"
51 #include "chrome/installer/util/master_preferences.h"
52 #include "chrome/installer/util/master_preferences_constants.h"
53 #include "chrome/installer/util/util_constants.h"
54 #include "chrome/installer/util/work_item.h"
56 using base::win::RegKey;
58 namespace {
60 // An enum used to tell QuickIsChromeRegistered() which level of registration
61 // the caller wants to confirm.
62 enum RegistrationConfirmationLevel {
63 // Only look for Chrome's ProgIds.
64 // This is sufficient when we are trying to determine the suffix of the
65 // currently running Chrome as shell integration registrations might not be
66 // present.
67 CONFIRM_PROGID_REGISTRATION = 0,
68 // Confirm that Chrome is fully integrated with Windows (i.e. registered with
69 // Defaut Programs). These registrations can be in HKCU as of Windows 8.
70 // Note: Shell registration implies ProgId registration.
71 CONFIRM_SHELL_REGISTRATION,
72 // Same as CONFIRM_SHELL_REGISTRATION, but only look in HKLM (used when
73 // uninstalling to know whether elevation is required to clean up the
74 // registry).
75 CONFIRM_SHELL_REGISTRATION_IN_HKLM,
78 const wchar_t kReinstallCommand[] = L"ReinstallCommand";
80 // Returns true if Chrome Metro is supported on this OS (Win 8 8370 or greater).
81 // TODO(gab): Change this to a simple check for Win 8 once old Win8 builds
82 // become irrelevant.
83 bool IsChromeMetroSupported() {
84 OSVERSIONINFOEX min_version_info = {};
85 min_version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
86 min_version_info.dwMajorVersion = 6;
87 min_version_info.dwMinorVersion = 2;
88 min_version_info.dwBuildNumber = 8370;
89 min_version_info.wServicePackMajor = 0;
90 min_version_info.wServicePackMinor = 0;
92 DWORDLONG condition_mask = 0;
93 VER_SET_CONDITION(condition_mask, VER_MAJORVERSION, VER_GREATER_EQUAL);
94 VER_SET_CONDITION(condition_mask, VER_MINORVERSION, VER_GREATER_EQUAL);
95 VER_SET_CONDITION(condition_mask, VER_BUILDNUMBER, VER_GREATER_EQUAL);
96 VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
97 VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL);
99 DWORD type_mask = VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER |
100 VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR;
102 return VerifyVersionInfo(&min_version_info, type_mask, condition_mask) != 0;
105 // Returns the current (or installed) browser's ProgId (e.g.
106 // "ChromeHTML|suffix|").
107 // |suffix| can be the empty string.
108 base::string16 GetBrowserProgId(const base::string16& suffix) {
109 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
110 base::string16 chrome_html(dist->GetBrowserProgIdPrefix());
111 chrome_html.append(suffix);
113 // ProgIds cannot be longer than 39 characters.
114 // Ref: http://msdn.microsoft.com/en-us/library/aa911706.aspx.
115 // Make all new registrations comply with this requirement (existing
116 // registrations must be preserved).
117 base::string16 new_style_suffix;
118 if (ShellUtil::GetUserSpecificRegistrySuffix(&new_style_suffix) &&
119 suffix == new_style_suffix && chrome_html.length() > 39) {
120 NOTREACHED();
121 chrome_html.erase(39);
123 return chrome_html;
126 // This class is used to initialize and cache a base 32 encoding of the md5 hash
127 // of this user's sid preceded by a dot.
128 // This is guaranteed to be unique on the machine and 27 characters long
129 // (including the '.').
130 // This is then meant to be used as a suffix on all registrations that may
131 // conflict with another user-level Chrome install.
132 class UserSpecificRegistrySuffix {
133 public:
134 // All the initialization is done in the constructor to be able to build the
135 // suffix in a thread-safe manner when used in conjunction with a
136 // LazyInstance.
137 UserSpecificRegistrySuffix();
139 // Sets |suffix| to the pre-computed suffix cached in this object.
140 // Returns true unless the initialization originally failed.
141 bool GetSuffix(base::string16* suffix);
143 private:
144 base::string16 suffix_;
146 DISALLOW_COPY_AND_ASSIGN(UserSpecificRegistrySuffix);
147 }; // class UserSpecificRegistrySuffix
149 UserSpecificRegistrySuffix::UserSpecificRegistrySuffix() {
150 base::string16 user_sid;
151 if (!base::win::GetUserSidString(&user_sid)) {
152 NOTREACHED();
153 return;
155 COMPILE_ASSERT(sizeof(base::MD5Digest) == 16, size_of_MD5_not_as_expected_);
156 base::MD5Digest md5_digest;
157 std::string user_sid_ascii(base::UTF16ToASCII(user_sid));
158 base::MD5Sum(user_sid_ascii.c_str(), user_sid_ascii.length(), &md5_digest);
159 const base::string16 base32_md5(
160 ShellUtil::ByteArrayToBase32(md5_digest.a, arraysize(md5_digest.a)));
161 // The value returned by the base32 algorithm above must never change and
162 // must always be 26 characters long (i.e. if someone ever moves this to
163 // base and implements the full base32 algorithm (i.e. with appended '='
164 // signs in the output), they must provide a flag to allow this method to
165 // still request the output with no appended '=' signs).
166 DCHECK_EQ(base32_md5.length(), 26U);
167 suffix_.reserve(base32_md5.length() + 1);
168 suffix_.assign(1, L'.');
169 suffix_.append(base32_md5);
172 bool UserSpecificRegistrySuffix::GetSuffix(base::string16* suffix) {
173 if (suffix_.empty()) {
174 NOTREACHED();
175 return false;
177 suffix->assign(suffix_);
178 return true;
181 // This class represents a single registry entry. The objective is to
182 // encapsulate all the registry entries required for registering Chrome at one
183 // place. This class can not be instantiated outside the class and the objects
184 // of this class type can be obtained only by calling a static method of this
185 // class.
186 class RegistryEntry {
187 public:
188 // A bit-field enum of places to look for this key in the Windows registry.
189 enum LookForIn {
190 LOOK_IN_HKCU = 1 << 0,
191 LOOK_IN_HKLM = 1 << 1,
192 LOOK_IN_HKCU_THEN_HKLM = LOOK_IN_HKCU | LOOK_IN_HKLM,
195 // Details about a Windows application, to be entered into the registry for
196 // the purpose of file associations.
197 struct ApplicationInfo {
198 ApplicationInfo() : file_type_icon_index(0), application_icon_index(0) {}
200 // The ProgId used by Windows for file associations with this application.
201 // Must not be empty or start with a '.'.
202 base::string16 prog_id;
203 // The friendly name, and the path of the icon that will be used for files
204 // of these types when associated with this application by default. (They
205 // are NOT the name/icon that will represent the application under the Open
206 // With menu.)
207 base::string16 file_type_name;
208 base::FilePath 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 base::FilePath application_icon_path;
222 int application_icon_index;
223 base::string16 application_description;
224 base::string16 publisher_name;
226 // The CLSID for the application's DelegateExecute handler. May be empty.
227 base::string16 delegate_clsid;
230 // Returns the Windows browser client registration key for Chrome. For
231 // example: "Software\Clients\StartMenuInternet\Chromium[.user]". Strictly
232 // speaking, we should use the name of the executable (e.g., "chrome.exe"),
233 // but that ship has sailed. The cost of switching now is re-prompting users
234 // to make Chrome their default browser, which isn't polite. |suffix| is the
235 // user-specific registration suffix; see GetUserSpecificDefaultBrowserSuffix
236 // in shell_util.h for details.
237 static base::string16 GetBrowserClientKey(BrowserDistribution* dist,
238 const base::string16& suffix) {
239 DCHECK(suffix.empty() || suffix[0] == L'.');
240 return base::string16(ShellUtil::kRegStartMenuInternet)
241 .append(1, L'\\')
242 .append(dist->GetBaseAppName())
243 .append(suffix);
246 // Returns the Windows Default Programs capabilities key for Chrome. For
247 // example:
248 // "Software\Clients\StartMenuInternet\Chromium[.user]\Capabilities".
249 static base::string16 GetCapabilitiesKey(BrowserDistribution* dist,
250 const base::string16& suffix) {
251 return GetBrowserClientKey(dist, suffix).append(L"\\Capabilities");
254 // This method returns a list of all the registry entries that
255 // are needed to register this installation's ProgId and AppId.
256 // These entries need to be registered in HKLM prior to Win8.
257 static void GetChromeProgIdEntries(BrowserDistribution* dist,
258 const base::FilePath& chrome_exe,
259 const base::string16& suffix,
260 ScopedVector<RegistryEntry>* entries) {
261 int chrome_icon_index =
262 dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME);
264 ApplicationInfo app_info;
265 app_info.prog_id = GetBrowserProgId(suffix);
266 app_info.file_type_name = dist->GetBrowserProgIdDesc();
267 // File types associated with Chrome are just given the Chrome icon.
268 app_info.file_type_icon_path = chrome_exe;
269 app_info.file_type_icon_index = chrome_icon_index;
270 app_info.command_line = ShellUtil::GetChromeShellOpenCmd(chrome_exe);
271 // For user-level installs: entries for the app id will be in HKCU; thus we
272 // do not need a suffix on those entries.
273 app_info.app_id = ShellUtil::GetBrowserModelId(
274 dist, InstallUtil::IsPerUserInstall(chrome_exe));
276 // The command to execute when opening this application via the Metro UI.
277 base::string16 delegate_command(
278 ShellUtil::GetChromeDelegateCommand(chrome_exe));
279 bool set_delegate_execute =
280 IsChromeMetroSupported() &&
281 dist->GetCommandExecuteImplClsid(&app_info.delegate_clsid);
283 // TODO(grt): http://crbug.com/75152 Write a reference to a localized
284 // resource for name, description, and company.
285 app_info.application_name = dist->GetDisplayName();
286 app_info.application_icon_path = chrome_exe;
287 app_info.application_icon_index = chrome_icon_index;
288 app_info.application_description = dist->GetAppDescription();
289 app_info.publisher_name = dist->GetPublisherName();
291 GetProgIdEntries(app_info, entries);
293 // DelegateExecute ProgId. Needed for Chrome Metro in Windows 8. This is
294 // only needed for registring a web browser, not for general associations.
295 if (set_delegate_execute) {
296 base::string16 model_id_shell(ShellUtil::kRegClasses);
297 model_id_shell.push_back(base::FilePath::kSeparators[0]);
298 model_id_shell.append(app_info.app_id);
299 model_id_shell.append(ShellUtil::kRegExePath);
300 model_id_shell.append(ShellUtil::kRegShellPath);
302 // <root hkey>\Software\Classes\<app_id>\.exe\shell @=open
303 entries->push_back(new RegistryEntry(model_id_shell,
304 ShellUtil::kRegVerbOpen));
306 // Each of Chrome's shortcuts has an appid; which, as of Windows 8, is
307 // registered to handle some verbs. This registration has the side-effect
308 // that these verbs now show up in the shortcut's context menu. We
309 // mitigate this side-effect by making the context menu entries
310 // user readable/localized strings. See relevant MSDN article:
311 // http://msdn.microsoft.com/en-US/library/windows/desktop/cc144171.aspx
312 const struct {
313 const wchar_t* verb;
314 int name_id;
315 } verbs[] = {
316 { ShellUtil::kRegVerbOpen, -1 },
317 { ShellUtil::kRegVerbOpenNewWindow, IDS_SHORTCUT_NEW_WINDOW_BASE },
319 for (size_t i = 0; i < arraysize(verbs); ++i) {
320 base::string16 sub_path(model_id_shell);
321 sub_path.push_back(base::FilePath::kSeparators[0]);
322 sub_path.append(verbs[i].verb);
324 // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb>
325 if (verbs[i].name_id != -1) {
326 // TODO(grt): http://crbug.com/75152 Write a reference to a localized
327 // resource.
328 base::string16 verb_name(
329 installer::GetLocalizedString(verbs[i].name_id));
330 entries->push_back(new RegistryEntry(sub_path, verb_name.c_str()));
332 entries->push_back(new RegistryEntry(
333 sub_path, L"CommandId", L"Browser.Launch"));
335 sub_path.push_back(base::FilePath::kSeparators[0]);
336 sub_path.append(ShellUtil::kRegCommand);
338 // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb>\command
339 entries->push_back(new RegistryEntry(sub_path, delegate_command));
340 entries->push_back(new RegistryEntry(
341 sub_path, ShellUtil::kRegDelegateExecute, app_info.delegate_clsid));
346 // Gets the registry entries to register an application in the Windows
347 // registry. |app_info| provides all of the information needed.
348 static void GetProgIdEntries(const ApplicationInfo& app_info,
349 ScopedVector<RegistryEntry>* entries) {
350 // Basic sanity checks.
351 DCHECK(!app_info.prog_id.empty());
352 DCHECK_NE(L'.', app_info.prog_id[0]);
354 // File association ProgId
355 base::string16 prog_id_path(ShellUtil::kRegClasses);
356 prog_id_path.push_back(base::FilePath::kSeparators[0]);
357 prog_id_path.append(app_info.prog_id);
358 entries->push_back(
359 new RegistryEntry(prog_id_path, app_info.file_type_name));
360 entries->push_back(new RegistryEntry(
361 prog_id_path + ShellUtil::kRegDefaultIcon,
362 ShellUtil::FormatIconLocation(app_info.file_type_icon_path,
363 app_info.file_type_icon_index)));
364 entries->push_back(new RegistryEntry(
365 prog_id_path + ShellUtil::kRegShellOpen, app_info.command_line));
366 if (!app_info.delegate_clsid.empty()) {
367 entries->push_back(
368 new RegistryEntry(prog_id_path + ShellUtil::kRegShellOpen,
369 ShellUtil::kRegDelegateExecute,
370 app_info.delegate_clsid));
373 // The following entries are required as of Windows 8, but do not
374 // depend on the DelegateExecute verb handler being set.
375 if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
376 if (!app_info.app_id.empty()) {
377 entries->push_back(new RegistryEntry(
378 prog_id_path, ShellUtil::kRegAppUserModelId, app_info.app_id));
381 // Add \Software\Classes\<prog_id>\Application entries
382 base::string16 application_path(prog_id_path +
383 ShellUtil::kRegApplication);
384 if (!app_info.app_id.empty()) {
385 entries->push_back(new RegistryEntry(
386 application_path, ShellUtil::kRegAppUserModelId, app_info.app_id));
388 if (!app_info.application_icon_path.empty()) {
389 entries->push_back(new RegistryEntry(
390 application_path,
391 ShellUtil::kRegApplicationIcon,
392 ShellUtil::FormatIconLocation(app_info.application_icon_path,
393 app_info.application_icon_index)));
395 if (!app_info.application_name.empty()) {
396 entries->push_back(new RegistryEntry(application_path,
397 ShellUtil::kRegApplicationName,
398 app_info.application_name));
400 if (!app_info.application_description.empty()) {
401 entries->push_back(
402 new RegistryEntry(application_path,
403 ShellUtil::kRegApplicationDescription,
404 app_info.application_description));
406 if (!app_info.publisher_name.empty()) {
407 entries->push_back(new RegistryEntry(application_path,
408 ShellUtil::kRegApplicationCompany,
409 app_info.publisher_name));
414 // This method returns a list of the registry entries needed to declare a
415 // capability of handling a protocol on Windows.
416 static void GetProtocolCapabilityEntries(
417 BrowserDistribution* dist,
418 const base::string16& suffix,
419 const base::string16& protocol,
420 ScopedVector<RegistryEntry>* entries) {
421 entries->push_back(new RegistryEntry(
422 GetCapabilitiesKey(dist, suffix).append(L"\\URLAssociations"),
423 protocol, GetBrowserProgId(suffix)));
426 // This method returns a list of the registry entries required to register
427 // this installation in "RegisteredApplications" on Windows (to appear in
428 // Default Programs, StartMenuInternet, etc.).
429 // These entries need to be registered in HKLM prior to Win8.
430 // If |suffix| is not empty, these entries are guaranteed to be unique on this
431 // machine.
432 static void GetShellIntegrationEntries(BrowserDistribution* dist,
433 const base::FilePath& chrome_exe,
434 const base::string16& suffix,
435 ScopedVector<RegistryEntry>* entries) {
436 const base::string16 icon_path(
437 ShellUtil::FormatIconLocation(
438 chrome_exe,
439 dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME)));
440 const base::string16 quoted_exe_path(L"\"" + chrome_exe.value() + L"\"");
442 // Register for the Start Menu "Internet" link (pre-Win7).
443 const base::string16 start_menu_entry(GetBrowserClientKey(dist, suffix));
444 // Register Chrome's display name.
445 // TODO(grt): http://crbug.com/75152 Also set LocalizedString; see
446 // http://msdn.microsoft.com/en-us/library/windows/desktop/cc144109(v=VS.85).aspx#registering_the_display_name
447 entries->push_back(new RegistryEntry(
448 start_menu_entry, dist->GetDisplayName()));
449 // Register the "open" verb for launching Chrome via the "Internet" link.
450 entries->push_back(new RegistryEntry(
451 start_menu_entry + ShellUtil::kRegShellOpen, quoted_exe_path));
452 // Register Chrome's icon for the Start Menu "Internet" link.
453 entries->push_back(new RegistryEntry(
454 start_menu_entry + ShellUtil::kRegDefaultIcon, icon_path));
456 // Register installation information.
457 base::string16 install_info(start_menu_entry + L"\\InstallInfo");
458 // Note: not using CommandLine since it has ambiguous rules for quoting
459 // strings.
460 entries->push_back(new RegistryEntry(install_info, kReinstallCommand,
461 quoted_exe_path + L" --" +
462 base::ASCIIToUTF16(switches::kMakeDefaultBrowser)));
463 entries->push_back(new RegistryEntry(install_info, L"HideIconsCommand",
464 quoted_exe_path + L" --" +
465 base::ASCIIToUTF16(switches::kHideIcons)));
466 entries->push_back(new RegistryEntry(install_info, L"ShowIconsCommand",
467 quoted_exe_path + L" --" +
468 base::ASCIIToUTF16(switches::kShowIcons)));
469 entries->push_back(new RegistryEntry(install_info, L"IconsVisible", 1));
471 // Register with Default Programs.
472 const base::string16 reg_app_name(dist->GetBaseAppName().append(suffix));
473 // Tell Windows where to find Chrome's Default Programs info.
474 const base::string16 capabilities(GetCapabilitiesKey(dist, suffix));
475 entries->push_back(new RegistryEntry(ShellUtil::kRegRegisteredApplications,
476 reg_app_name, capabilities));
477 // Write out Chrome's Default Programs info.
478 // TODO(grt): http://crbug.com/75152 Write a reference to a localized
479 // resource rather than this.
480 entries->push_back(new RegistryEntry(
481 capabilities, ShellUtil::kRegApplicationDescription,
482 dist->GetLongAppDescription()));
483 entries->push_back(new RegistryEntry(
484 capabilities, ShellUtil::kRegApplicationIcon, icon_path));
485 entries->push_back(new RegistryEntry(
486 capabilities, ShellUtil::kRegApplicationName,
487 dist->GetDisplayName()));
489 entries->push_back(new RegistryEntry(capabilities + L"\\Startmenu",
490 L"StartMenuInternet", reg_app_name));
492 const base::string16 html_prog_id(GetBrowserProgId(suffix));
493 for (int i = 0; ShellUtil::kPotentialFileAssociations[i] != NULL; i++) {
494 entries->push_back(new RegistryEntry(
495 capabilities + L"\\FileAssociations",
496 ShellUtil::kPotentialFileAssociations[i], html_prog_id));
498 for (int i = 0; ShellUtil::kPotentialProtocolAssociations[i] != NULL;
499 i++) {
500 entries->push_back(new RegistryEntry(
501 capabilities + L"\\URLAssociations",
502 ShellUtil::kPotentialProtocolAssociations[i], html_prog_id));
506 // This method returns a list of the registry entries required for this
507 // installation to be registered in the Windows shell.
508 // In particular:
509 // - App Paths
510 // http://msdn.microsoft.com/en-us/library/windows/desktop/ee872121
511 // - File Associations
512 // http://msdn.microsoft.com/en-us/library/bb166549
513 // These entries need to be registered in HKLM prior to Win8.
514 static void GetChromeAppRegistrationEntries(
515 const base::FilePath& chrome_exe,
516 const base::string16& suffix,
517 ScopedVector<RegistryEntry>* entries) {
518 base::string16 app_path_key(ShellUtil::kAppPathsRegistryKey);
519 app_path_key.push_back(base::FilePath::kSeparators[0]);
520 app_path_key.append(chrome_exe.BaseName().value());
521 entries->push_back(new RegistryEntry(app_path_key, chrome_exe.value()));
522 entries->push_back(new RegistryEntry(app_path_key,
523 ShellUtil::kAppPathsRegistryPathName,
524 chrome_exe.DirName().value()));
526 const base::string16 html_prog_id(GetBrowserProgId(suffix));
527 for (int i = 0; ShellUtil::kPotentialFileAssociations[i] != NULL; i++) {
528 GetAppExtRegistrationEntries(
529 html_prog_id, ShellUtil::kPotentialFileAssociations[i], entries);
533 // Gets the registry entries to register an application as a handler for a
534 // particular file extension. |prog_id| is the ProgId used by Windows for the
535 // application. |ext| is the file extension, which must begin with a '.'.
536 static void GetAppExtRegistrationEntries(
537 const base::string16& prog_id,
538 const base::string16& ext,
539 ScopedVector<RegistryEntry>* entries) {
540 // In HKEY_CURRENT_USER\Software\Classes\EXT\OpenWithProgids, create an
541 // empty value with this class's ProgId.
542 base::string16 key_name(ShellUtil::kRegClasses);
543 key_name.push_back(base::FilePath::kSeparators[0]);
544 key_name.append(ext);
545 key_name.push_back(base::FilePath::kSeparators[0]);
546 key_name.append(ShellUtil::kRegOpenWithProgids);
547 entries->push_back(new RegistryEntry(key_name, prog_id, base::string16()));
550 // Gets the registry entries to register an application as the default handler
551 // for a particular file extension. |prog_id| is the ProgId used by Windows
552 // for the application. |ext| is the file extension, which must begin with a
553 // '.'. If |overwrite_existing|, always sets the default handler; otherwise
554 // only sets if there is no existing default.
556 // This has no effect on Windows 8. Windows 8 ignores the default and lets the
557 // user choose. If there is only one handler for a file, it will automatically
558 // become the default. Otherwise, the first time the user opens a file, they
559 // are presented with the dialog to set the default handler. (This is roughly
560 // equivalent to being called with |overwrite_existing| false.)
561 static void GetAppDefaultRegistrationEntries(
562 const base::string16& prog_id,
563 const base::string16& ext,
564 bool overwrite_existing,
565 ScopedVector<RegistryEntry>* entries) {
566 // Set the default value of HKEY_CURRENT_USER\Software\Classes\EXT to this
567 // class's name.
568 base::string16 key_name(ShellUtil::kRegClasses);
569 key_name.push_back(base::FilePath::kSeparators[0]);
570 key_name.append(ext);
571 scoped_ptr<RegistryEntry> default_association(
572 new RegistryEntry(key_name, prog_id));
573 if (overwrite_existing ||
574 !default_association->KeyExistsInRegistry(
575 RegistryEntry::LOOK_IN_HKCU)) {
576 entries->push_back(default_association.release());
580 // This method returns a list of all the user level registry entries that
581 // are needed to make Chromium the default handler for a protocol on XP.
582 static void GetXPStyleUserProtocolEntries(
583 const base::string16& protocol,
584 const base::string16& chrome_icon,
585 const base::string16& chrome_open,
586 ScopedVector<RegistryEntry>* entries) {
587 // Protocols associations.
588 base::string16 url_key(ShellUtil::kRegClasses);
589 url_key.push_back(base::FilePath::kSeparators[0]);
590 url_key.append(protocol);
592 // This registry value tells Windows that this 'class' is a URL scheme
593 // so IE, explorer and other apps will route it to our handler.
594 // <root hkey>\Software\Classes\<protocol>\URL Protocol
595 entries->push_back(new RegistryEntry(url_key,
596 ShellUtil::kRegUrlProtocol, base::string16()));
598 // <root hkey>\Software\Classes\<protocol>\DefaultIcon
599 base::string16 icon_key = url_key + ShellUtil::kRegDefaultIcon;
600 entries->push_back(new RegistryEntry(icon_key, chrome_icon));
602 // <root hkey>\Software\Classes\<protocol>\shell\open\command
603 base::string16 shell_key = url_key + ShellUtil::kRegShellOpen;
604 entries->push_back(new RegistryEntry(shell_key, chrome_open));
606 // <root hkey>\Software\Classes\<protocol>\shell\open\ddeexec
607 base::string16 dde_key = url_key + L"\\shell\\open\\ddeexec";
608 entries->push_back(new RegistryEntry(dde_key, base::string16()));
610 // <root hkey>\Software\Classes\<protocol>\shell\@
611 base::string16 protocol_shell_key = url_key + ShellUtil::kRegShellPath;
612 entries->push_back(new RegistryEntry(protocol_shell_key, L"open"));
615 // This method returns a list of all the user level registry entries that
616 // are needed to make Chromium default browser on XP.
617 // Some of these entries are irrelevant in recent versions of Windows, but
618 // we register them anyways as some legacy apps are hardcoded to lookup those
619 // values.
620 static void GetXPStyleDefaultBrowserUserEntries(
621 BrowserDistribution* dist,
622 const base::FilePath& chrome_exe,
623 const base::string16& suffix,
624 ScopedVector<RegistryEntry>* entries) {
625 // File extension associations.
626 base::string16 html_prog_id(GetBrowserProgId(suffix));
627 for (int i = 0; ShellUtil::kDefaultFileAssociations[i] != NULL; i++) {
628 GetAppDefaultRegistrationEntries(
629 html_prog_id, ShellUtil::kDefaultFileAssociations[i], true, entries);
632 // Protocols associations.
633 base::string16 chrome_open = ShellUtil::GetChromeShellOpenCmd(chrome_exe);
634 base::string16 chrome_icon =
635 ShellUtil::FormatIconLocation(
636 chrome_exe,
637 dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME));
638 for (int i = 0; ShellUtil::kBrowserProtocolAssociations[i] != NULL; i++) {
639 GetXPStyleUserProtocolEntries(ShellUtil::kBrowserProtocolAssociations[i],
640 chrome_icon, chrome_open, entries);
643 // start->Internet shortcut.
644 base::string16 start_menu(ShellUtil::kRegStartMenuInternet);
645 base::string16 app_name = dist->GetBaseAppName() + suffix;
646 entries->push_back(new RegistryEntry(start_menu, app_name));
649 // Generate work_item tasks required to create current registry entry and
650 // add them to the given work item list.
651 void AddToWorkItemList(HKEY root, WorkItemList *items) const {
652 items->AddCreateRegKeyWorkItem(root, key_path_, WorkItem::kWow64Default);
653 if (is_string_) {
654 items->AddSetRegValueWorkItem(
655 root, key_path_, WorkItem::kWow64Default, name_, value_, true);
656 } else {
657 items->AddSetRegValueWorkItem(
658 root, key_path_, WorkItem::kWow64Default, name_, int_value_, true);
662 // Checks if the current registry entry exists in HKCU\|key_path_|\|name_|
663 // and value is |value_|. If the key does NOT exist in HKCU, checks for
664 // the correct name and value in HKLM.
665 // |look_for_in| specifies roots (HKCU and/or HKLM) in which to look for the
666 // key, unspecified roots are not looked into (i.e. the the key is assumed not
667 // to exist in them).
668 // |look_for_in| must at least specify one root to look into.
669 // If |look_for_in| is LOOK_IN_HKCU_THEN_HKLM, this method mimics Windows'
670 // behavior when searching in HKCR (HKCU takes precedence over HKLM). For
671 // registrations outside of HKCR on versions of Windows prior to Win8,
672 // Chrome's values go in HKLM. This function will make unnecessary (but
673 // harmless) queries into HKCU in that case.
674 bool ExistsInRegistry(uint32 look_for_in) const {
675 DCHECK(look_for_in);
677 RegistryStatus status = DOES_NOT_EXIST;
678 if (look_for_in & LOOK_IN_HKCU)
679 status = StatusInRegistryUnderRoot(HKEY_CURRENT_USER);
680 if (status == DOES_NOT_EXIST && (look_for_in & LOOK_IN_HKLM))
681 status = StatusInRegistryUnderRoot(HKEY_LOCAL_MACHINE);
682 return status == SAME_VALUE;
685 // Checks if the current registry entry exists in \|key_path_|\|name_|,
686 // regardless of value. Same lookup rules as ExistsInRegistry.
687 // Unlike ExistsInRegistry, this returns true if some other value is present
688 // with the same key.
689 bool KeyExistsInRegistry(uint32 look_for_in) const {
690 DCHECK(look_for_in);
692 RegistryStatus status = DOES_NOT_EXIST;
693 if (look_for_in & LOOK_IN_HKCU)
694 status = StatusInRegistryUnderRoot(HKEY_CURRENT_USER);
695 if (status == DOES_NOT_EXIST && (look_for_in & LOOK_IN_HKLM))
696 status = StatusInRegistryUnderRoot(HKEY_LOCAL_MACHINE);
697 return status != DOES_NOT_EXIST;
700 private:
701 // States this RegistryKey can be in compared to the registry.
702 enum RegistryStatus {
703 // |name_| does not exist in the registry
704 DOES_NOT_EXIST,
705 // |name_| exists, but its value != |value_|
706 DIFFERENT_VALUE,
707 // |name_| exists and its value is |value_|
708 SAME_VALUE,
711 // Create a object that represent default value of a key
712 RegistryEntry(const base::string16& key_path, const base::string16& value)
713 : key_path_(key_path), name_(),
714 is_string_(true), value_(value), int_value_(0) {
717 // Create a object that represent a key of type REG_SZ
718 RegistryEntry(const base::string16& key_path, const base::string16& name,
719 const base::string16& value)
720 : key_path_(key_path), name_(name),
721 is_string_(true), value_(value), int_value_(0) {
724 // Create a object that represent a key of integer type
725 RegistryEntry(const base::string16& key_path, const base::string16& name,
726 DWORD value)
727 : key_path_(key_path), name_(name),
728 is_string_(false), value_(), int_value_(value) {
731 base::string16 key_path_; // key path for the registry entry
732 base::string16 name_; // name of the registry entry
733 bool is_string_; // true if current registry entry is of type REG_SZ
734 base::string16 value_; // string value (useful if is_string_ = true)
735 DWORD int_value_; // integer value (useful if is_string_ = false)
737 // Helper function for ExistsInRegistry().
738 // Returns the RegistryStatus of the current registry entry in
739 // |root|\|key_path_|\|name_|.
740 RegistryStatus StatusInRegistryUnderRoot(HKEY root) const {
741 RegKey key(root, key_path_.c_str(), KEY_QUERY_VALUE);
742 bool found = false;
743 bool correct_value = false;
744 if (is_string_) {
745 base::string16 read_value;
746 found = key.ReadValue(name_.c_str(), &read_value) == ERROR_SUCCESS;
747 if (found) {
748 correct_value = read_value.size() == value_.size() &&
749 ::CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE,
750 read_value.data(),
751 base::saturated_cast<int>(read_value.size()),
752 value_.data(),
753 base::saturated_cast<int>(value_.size()))
754 == CSTR_EQUAL;
756 } else {
757 DWORD read_value;
758 found = key.ReadValueDW(name_.c_str(), &read_value) == ERROR_SUCCESS;
759 if (found)
760 correct_value = read_value == int_value_;
762 return found ?
763 (correct_value ? SAME_VALUE : DIFFERENT_VALUE) : DOES_NOT_EXIST;
766 DISALLOW_COPY_AND_ASSIGN(RegistryEntry);
767 }; // class RegistryEntry
770 // This method converts all the RegistryEntries from the given list to
771 // Set/CreateRegWorkItems and runs them using WorkItemList.
772 bool AddRegistryEntries(HKEY root, const ScopedVector<RegistryEntry>& entries) {
773 scoped_ptr<WorkItemList> items(WorkItem::CreateWorkItemList());
775 for (ScopedVector<RegistryEntry>::const_iterator itr = entries.begin();
776 itr != entries.end(); ++itr)
777 (*itr)->AddToWorkItemList(root, items.get());
779 // Apply all the registry changes and if there is a problem, rollback
780 if (!items->Do()) {
781 items->Rollback();
782 return false;
784 return true;
787 // Checks that all |entries| are present on this computer.
788 // |look_for_in| is passed to RegistryEntry::ExistsInRegistry(). Documentation
789 // for it can be found there.
790 bool AreEntriesRegistered(const ScopedVector<RegistryEntry>& entries,
791 uint32 look_for_in) {
792 bool registered = true;
793 for (ScopedVector<RegistryEntry>::const_iterator itr = entries.begin();
794 registered && itr != entries.end(); ++itr) {
795 // We do not need registered = registered && ... since the loop condition
796 // is set to exit early.
797 registered = (*itr)->ExistsInRegistry(look_for_in);
799 return registered;
802 // Checks that all required registry entries for Chrome are already present
803 // on this computer. See RegistryEntry::ExistsInRegistry for the behavior of
804 // |look_for_in|.
805 // Note: between r133333 and r154145 we were registering parts of Chrome in HKCU
806 // and parts in HKLM for user-level installs; we now always register everything
807 // under a single registry root. Not doing so caused http://crbug.com/144910 for
808 // users who first-installed Chrome in that revision range (those users are
809 // still impacted by http://crbug.com/144910). This method will keep returning
810 // true for affected users (i.e. who have all the registrations, but over both
811 // registry roots).
812 bool IsChromeRegistered(BrowserDistribution* dist,
813 const base::FilePath& chrome_exe,
814 const base::string16& suffix,
815 uint32 look_for_in) {
816 ScopedVector<RegistryEntry> entries;
817 RegistryEntry::GetChromeProgIdEntries(dist, chrome_exe, suffix, &entries);
818 RegistryEntry::GetShellIntegrationEntries(dist, chrome_exe, suffix, &entries);
819 RegistryEntry::GetChromeAppRegistrationEntries(chrome_exe, suffix, &entries);
820 return AreEntriesRegistered(entries, look_for_in);
823 // This method checks if Chrome is already registered on the local machine
824 // for the requested protocol. It just checks the one value required for this.
825 // See RegistryEntry::ExistsInRegistry for the behavior of |look_for_in|.
826 bool IsChromeRegisteredForProtocol(BrowserDistribution* dist,
827 const base::string16& suffix,
828 const base::string16& protocol,
829 uint32 look_for_in) {
830 ScopedVector<RegistryEntry> entries;
831 RegistryEntry::GetProtocolCapabilityEntries(dist, suffix, protocol, &entries);
832 return AreEntriesRegistered(entries, look_for_in);
835 // This method registers Chrome on Vista by launching an elevated setup.exe.
836 // That will show the user the standard Vista elevation prompt. If the user
837 // accepts it the new process will make the necessary changes and return SUCCESS
838 // that we capture and return.
839 // If protocol is non-empty we will also register Chrome as being capable of
840 // handling the protocol.
841 bool ElevateAndRegisterChrome(BrowserDistribution* dist,
842 const base::FilePath& chrome_exe,
843 const base::string16& suffix,
844 const base::string16& protocol) {
845 // Only user-level installs prior to Windows 8 should need to elevate to
846 // register.
847 DCHECK(InstallUtil::IsPerUserInstall(chrome_exe));
848 DCHECK_LT(base::win::GetVersion(), base::win::VERSION_WIN8);
849 base::FilePath exe_path = chrome_exe.DirName().Append(installer::kSetupExe);
850 if (!base::PathExists(exe_path)) {
851 HKEY reg_root = InstallUtil::IsPerUserInstall(chrome_exe) ?
852 HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
853 RegKey key(reg_root,
854 dist->GetUninstallRegPath().c_str(),
855 KEY_READ | KEY_WOW64_32KEY);
856 base::string16 uninstall_string;
857 key.ReadValue(installer::kUninstallStringField, &uninstall_string);
858 base::CommandLine command_line =
859 base::CommandLine::FromString(uninstall_string);
860 exe_path = command_line.GetProgram();
863 if (base::PathExists(exe_path)) {
864 base::CommandLine cmd(exe_path);
865 cmd.AppendSwitchPath(installer::switches::kRegisterChromeBrowser,
866 chrome_exe);
867 if (!suffix.empty()) {
868 cmd.AppendSwitchNative(
869 installer::switches::kRegisterChromeBrowserSuffix, suffix);
872 if (!protocol.empty()) {
873 cmd.AppendSwitchNative(
874 installer::switches::kRegisterURLProtocol, protocol);
877 DWORD ret_val = 0;
878 InstallUtil::ExecuteExeAsAdmin(cmd, &ret_val);
879 if (ret_val == 0)
880 return true;
882 return false;
885 // Launches the Windows 'settings' modern app with the 'default apps' view
886 // focused. This only works for Windows 8 and Windows 10. The appModelId
887 // looks arbitrary but it is the same in Win8 and Win10 previews. There
888 // is no easy way to retrieve the appModelId from the registry.
889 bool LaunchDefaultAppsSettingsModernDialog() {
890 base::win::ScopedComPtr<IApplicationActivationManager> activator;
891 HRESULT hr = activator.CreateInstance(CLSID_ApplicationActivationManager);
892 if (SUCCEEDED(hr)) {
893 DWORD pid = 0;
894 CoAllowSetForegroundWindow(activator.get(), nullptr);
895 hr = activator->ActivateApplication(
896 L"windows.immersivecontrolpanel_cw5n1h2txyewy"
897 L"!microsoft.windows.immersivecontrolpanel",
898 L"page=SettingsPageAppsDefaults", AO_NONE, &pid);
899 return SUCCEEDED(hr);
901 return false;
904 // Launches the Windows 7 and Windows 8 dialog for picking the application to
905 // handle the given protocol. Most importantly, this is used to set the default
906 // handler for http (and, implicitly with it, https). In that case it is also
907 // known as the 'how do you want to open webpages' dialog.
908 // It is required that Chrome be already *registered* for the given protocol.
909 bool LaunchSelectDefaultProtocolHandlerDialog(const wchar_t* protocol) {
910 DCHECK(protocol);
911 OPENASINFO open_as_info = {};
912 open_as_info.pcszFile = protocol;
913 open_as_info.oaifInFlags =
914 OAIF_URL_PROTOCOL | OAIF_FORCE_REGISTRATION | OAIF_REGISTER_EXT;
915 HRESULT hr = SHOpenWithDialog(NULL, &open_as_info);
916 DLOG_IF(WARNING, FAILED(hr)) << "Failed to set as default " << protocol
917 << " handler; hr=0x" << std::hex << hr;
918 if (FAILED(hr))
919 return false;
920 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
921 return true;
924 // Returns true if the current install's |chrome_exe| has been registered with
925 // |suffix|.
926 // |confirmation_level| is the level of verification desired as described in
927 // the RegistrationConfirmationLevel enum above.
928 // |suffix| can be the empty string (this is used to support old installs
929 // where we used to not suffix user-level installs if they were the first to
930 // request the non-suffixed registry entries on the machine).
931 // NOTE: This a quick check that only validates that a single registry entry
932 // points to |chrome_exe|. This should only be used at run-time to determine
933 // how Chrome is registered, not to know whether the registration is complete
934 // at install-time (IsChromeRegistered() can be used for that).
935 bool QuickIsChromeRegistered(BrowserDistribution* dist,
936 const base::FilePath& chrome_exe,
937 const base::string16& suffix,
938 RegistrationConfirmationLevel confirmation_level) {
939 // Get the appropriate key to look for based on the level desired.
940 base::string16 reg_key;
941 switch (confirmation_level) {
942 case CONFIRM_PROGID_REGISTRATION:
943 // Software\Classes\ChromeHTML|suffix|
944 reg_key = ShellUtil::kRegClasses;
945 reg_key.push_back(base::FilePath::kSeparators[0]);
946 reg_key.append(dist->GetBrowserProgIdPrefix());
947 reg_key.append(suffix);
948 break;
949 case CONFIRM_SHELL_REGISTRATION:
950 case CONFIRM_SHELL_REGISTRATION_IN_HKLM:
951 // Software\Clients\StartMenuInternet\Google Chrome|suffix|
952 reg_key = RegistryEntry::GetBrowserClientKey(dist, suffix);
953 break;
954 default:
955 NOTREACHED();
956 break;
958 reg_key.append(ShellUtil::kRegShellOpen);
960 // ProgId registrations are allowed to reside in HKCU for user-level installs
961 // (and values there have priority over values in HKLM). The same is true for
962 // shell integration entries as of Windows 8.
963 if (confirmation_level == CONFIRM_PROGID_REGISTRATION ||
964 (confirmation_level == CONFIRM_SHELL_REGISTRATION &&
965 base::win::GetVersion() >= base::win::VERSION_WIN8)) {
966 const RegKey key_hkcu(HKEY_CURRENT_USER, reg_key.c_str(), KEY_QUERY_VALUE);
967 base::string16 hkcu_value;
968 // If |reg_key| is present in HKCU, assert that it points to |chrome_exe|.
969 // Otherwise, fall back on an HKLM lookup below.
970 if (key_hkcu.ReadValue(L"", &hkcu_value) == ERROR_SUCCESS)
971 return InstallUtil::ProgramCompare(chrome_exe).Evaluate(hkcu_value);
974 // Assert that |reg_key| points to |chrome_exe| in HKLM.
975 const RegKey key_hklm(HKEY_LOCAL_MACHINE, reg_key.c_str(), KEY_QUERY_VALUE);
976 base::string16 hklm_value;
977 if (key_hklm.ReadValue(L"", &hklm_value) == ERROR_SUCCESS)
978 return InstallUtil::ProgramCompare(chrome_exe).Evaluate(hklm_value);
979 return false;
982 // Sets |suffix| to a 27 character string that is specific to this user on this
983 // machine (on user-level installs only).
984 // To support old-style user-level installs however, |suffix| is cleared if the
985 // user currently owns the non-suffixed HKLM registrations.
986 // |suffix| can also be set to the user's username if the current install is
987 // suffixed as per the old-style registrations.
988 // |suffix| is cleared on system-level installs.
989 // |suffix| should then be appended to all Chrome properties that may conflict
990 // with other Chrome user-level installs.
991 // Returns true unless one of the underlying calls fails.
992 bool GetInstallationSpecificSuffix(BrowserDistribution* dist,
993 const base::FilePath& chrome_exe,
994 base::string16* suffix) {
995 if (!InstallUtil::IsPerUserInstall(chrome_exe) ||
996 QuickIsChromeRegistered(dist, chrome_exe, base::string16(),
997 CONFIRM_SHELL_REGISTRATION)) {
998 // No suffix on system-level installs and user-level installs already
999 // registered with no suffix.
1000 suffix->clear();
1001 return true;
1004 // Get the old suffix for the check below.
1005 if (!ShellUtil::GetOldUserSpecificRegistrySuffix(suffix)) {
1006 NOTREACHED();
1007 return false;
1009 if (QuickIsChromeRegistered(dist, chrome_exe, *suffix,
1010 CONFIRM_SHELL_REGISTRATION)) {
1011 // Username suffix for installs that are suffixed as per the old-style.
1012 return true;
1015 return ShellUtil::GetUserSpecificRegistrySuffix(suffix);
1018 // Returns the root registry key (HKLM or HKCU) under which registrations must
1019 // be placed for this install. As of Windows 8 everything can go in HKCU for
1020 // per-user installs.
1021 HKEY DetermineRegistrationRoot(bool is_per_user) {
1022 return is_per_user && base::win::GetVersion() >= base::win::VERSION_WIN8 ?
1023 HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
1026 // Associates Chrome with supported protocols and file associations. This should
1027 // not be required on Vista+ but since some applications still read
1028 // Software\Classes\http key directly, we have to do this on Vista+ as well.
1029 bool RegisterChromeAsDefaultXPStyle(BrowserDistribution* dist,
1030 int shell_change,
1031 const base::FilePath& chrome_exe) {
1032 bool ret = true;
1033 ScopedVector<RegistryEntry> entries;
1034 RegistryEntry::GetXPStyleDefaultBrowserUserEntries(
1035 dist, chrome_exe,
1036 ShellUtil::GetCurrentInstallationSuffix(dist, chrome_exe), &entries);
1038 // Change the default browser for current user.
1039 if ((shell_change & ShellUtil::CURRENT_USER) &&
1040 !AddRegistryEntries(HKEY_CURRENT_USER, entries)) {
1041 ret = false;
1042 LOG(ERROR) << "Could not make Chrome default browser (XP/current user).";
1045 // Chrome as default browser at system level.
1046 if ((shell_change & ShellUtil::SYSTEM_LEVEL) &&
1047 !AddRegistryEntries(HKEY_LOCAL_MACHINE, entries)) {
1048 ret = false;
1049 LOG(ERROR) << "Could not make Chrome default browser (XP/system level).";
1052 return ret;
1055 // Associates Chrome with |protocol| in the registry. This should not be
1056 // required on Vista+ but since some applications still read these registry
1057 // keys directly, we have to do this on Vista+ as well.
1058 // See http://msdn.microsoft.com/library/aa767914.aspx for more details.
1059 bool RegisterChromeAsDefaultProtocolClientXPStyle(
1060 BrowserDistribution* dist,
1061 const base::FilePath& chrome_exe,
1062 const base::string16& protocol) {
1063 ScopedVector<RegistryEntry> entries;
1064 const base::string16 chrome_open(
1065 ShellUtil::GetChromeShellOpenCmd(chrome_exe));
1066 const base::string16 chrome_icon(
1067 ShellUtil::FormatIconLocation(
1068 chrome_exe,
1069 dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME)));
1070 RegistryEntry::GetXPStyleUserProtocolEntries(protocol, chrome_icon,
1071 chrome_open, &entries);
1072 // Change the default protocol handler for current user.
1073 if (!AddRegistryEntries(HKEY_CURRENT_USER, entries)) {
1074 LOG(ERROR) << "Could not make Chrome default protocol client (XP).";
1075 return false;
1078 return true;
1081 // Returns |properties.shortcut_name| if the property is set, otherwise it
1082 // returns dist->GetShortcutName(BrowserDistribution::SHORTCUT_CHROME). In any
1083 // case, it makes sure the return value is suffixed with ".lnk".
1084 base::string16 ExtractShortcutNameFromProperties(
1085 BrowserDistribution* dist,
1086 const ShellUtil::ShortcutProperties& properties) {
1087 DCHECK(dist);
1088 base::string16 shortcut_name;
1089 if (properties.has_shortcut_name()) {
1090 shortcut_name = properties.shortcut_name;
1091 } else {
1092 shortcut_name =
1093 dist->GetShortcutName(BrowserDistribution::SHORTCUT_CHROME);
1096 if (!base::EndsWith(shortcut_name, installer::kLnkExt,
1097 base::CompareCase::INSENSITIVE_ASCII))
1098 shortcut_name.append(installer::kLnkExt);
1100 return shortcut_name;
1103 // Converts ShellUtil::ShortcutOperation to the best-matching value in
1104 // base::win::ShortcutOperation.
1105 base::win::ShortcutOperation TranslateShortcutOperation(
1106 ShellUtil::ShortcutOperation operation) {
1107 switch (operation) {
1108 case ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS: // Falls through.
1109 case ShellUtil::SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL:
1110 return base::win::SHORTCUT_CREATE_ALWAYS;
1112 case ShellUtil::SHELL_SHORTCUT_UPDATE_EXISTING:
1113 return base::win::SHORTCUT_UPDATE_EXISTING;
1115 case ShellUtil::SHELL_SHORTCUT_REPLACE_EXISTING:
1116 return base::win::SHORTCUT_REPLACE_EXISTING;
1118 default:
1119 NOTREACHED();
1120 return base::win::SHORTCUT_REPLACE_EXISTING;
1124 // Returns a base::win::ShortcutProperties struct containing the properties
1125 // to set on the shortcut based on the provided ShellUtil::ShortcutProperties.
1126 base::win::ShortcutProperties TranslateShortcutProperties(
1127 const ShellUtil::ShortcutProperties& properties) {
1128 base::win::ShortcutProperties shortcut_properties;
1130 if (properties.has_target()) {
1131 shortcut_properties.set_target(properties.target);
1132 DCHECK(!properties.target.DirName().empty());
1133 shortcut_properties.set_working_dir(properties.target.DirName());
1136 if (properties.has_arguments())
1137 shortcut_properties.set_arguments(properties.arguments);
1139 if (properties.has_description())
1140 shortcut_properties.set_description(properties.description);
1142 if (properties.has_icon())
1143 shortcut_properties.set_icon(properties.icon, properties.icon_index);
1145 if (properties.has_app_id())
1146 shortcut_properties.set_app_id(properties.app_id);
1148 if (properties.has_dual_mode())
1149 shortcut_properties.set_dual_mode(properties.dual_mode);
1151 return shortcut_properties;
1154 // Cleans up an old verb (run) we used to register in
1155 // <root>\Software\Classes\Chrome<.suffix>\.exe\shell\run on Windows 8.
1156 void RemoveRunVerbOnWindows8(BrowserDistribution* dist,
1157 const base::FilePath& chrome_exe) {
1158 if (IsChromeMetroSupported()) {
1159 bool is_per_user_install = InstallUtil::IsPerUserInstall(chrome_exe);
1160 HKEY root_key = DetermineRegistrationRoot(is_per_user_install);
1161 // There's no need to rollback, so forgo the usual work item lists and just
1162 // remove the key from the registry.
1163 base::string16 run_verb_key(ShellUtil::kRegClasses);
1164 run_verb_key.push_back(base::FilePath::kSeparators[0]);
1165 run_verb_key.append(ShellUtil::GetBrowserModelId(
1166 dist, is_per_user_install));
1167 run_verb_key.append(ShellUtil::kRegExePath);
1168 run_verb_key.append(ShellUtil::kRegShellPath);
1169 run_verb_key.push_back(base::FilePath::kSeparators[0]);
1170 run_verb_key.append(ShellUtil::kRegVerbRun);
1171 InstallUtil::DeleteRegistryKey(root_key, run_verb_key,
1172 WorkItem::kWow64Default);
1176 // Gets the short (8.3) form of |path|, putting the result in |short_path| and
1177 // returning true on success. |short_path| is not modified on failure.
1178 bool ShortNameFromPath(const base::FilePath& path, base::string16* short_path) {
1179 DCHECK(short_path);
1180 base::string16 result(MAX_PATH, L'\0');
1181 DWORD short_length = GetShortPathName(path.value().c_str(), &result[0],
1182 result.size());
1183 if (short_length == 0 || short_length > result.size()) {
1184 PLOG(ERROR) << "Error getting short (8.3) path for " << path.value();
1185 return false;
1188 result.resize(short_length);
1189 short_path->swap(result);
1190 return true;
1193 // Probe using IApplicationAssociationRegistration::QueryCurrentDefault
1194 // (Windows 8); see ProbeProtocolHandlers. This mechanism is not suitable for
1195 // use on previous versions of Windows despite the presence of
1196 // QueryCurrentDefault on them since versions of Windows prior to Windows 8
1197 // did not perform validation on the ProgID registered as the current default.
1198 // As a result, stale ProgIDs could be returned, leading to false positives.
1199 ShellUtil::DefaultState ProbeCurrentDefaultHandlers(
1200 const base::FilePath& chrome_exe,
1201 const wchar_t* const* protocols,
1202 size_t num_protocols) {
1203 base::win::ScopedComPtr<IApplicationAssociationRegistration> registration;
1204 HRESULT hr = registration.CreateInstance(
1205 CLSID_ApplicationAssociationRegistration, NULL, CLSCTX_INPROC);
1206 if (FAILED(hr))
1207 return ShellUtil::UNKNOWN_DEFAULT;
1209 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
1210 base::string16 prog_id(dist->GetBrowserProgIdPrefix());
1211 prog_id += ShellUtil::GetCurrentInstallationSuffix(dist, chrome_exe);
1213 for (size_t i = 0; i < num_protocols; ++i) {
1214 base::win::ScopedCoMem<wchar_t> current_app;
1215 hr = registration->QueryCurrentDefault(protocols[i], AT_URLPROTOCOL,
1216 AL_EFFECTIVE, &current_app);
1217 if (FAILED(hr) || prog_id.compare(current_app) != 0)
1218 return ShellUtil::NOT_DEFAULT;
1221 return ShellUtil::IS_DEFAULT;
1224 // Probe using IApplicationAssociationRegistration::QueryAppIsDefault (Vista and
1225 // Windows 7); see ProbeProtocolHandlers.
1226 ShellUtil::DefaultState ProbeAppIsDefaultHandlers(
1227 const base::FilePath& chrome_exe,
1228 const wchar_t* const* protocols,
1229 size_t num_protocols) {
1230 base::win::ScopedComPtr<IApplicationAssociationRegistration> registration;
1231 HRESULT hr = registration.CreateInstance(
1232 CLSID_ApplicationAssociationRegistration, NULL, CLSCTX_INPROC);
1233 if (FAILED(hr))
1234 return ShellUtil::UNKNOWN_DEFAULT;
1236 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
1237 base::string16 app_name(ShellUtil::GetApplicationName(dist, chrome_exe));
1239 BOOL result;
1240 for (size_t i = 0; i < num_protocols; ++i) {
1241 result = TRUE;
1242 hr = registration->QueryAppIsDefault(protocols[i], AT_URLPROTOCOL,
1243 AL_EFFECTIVE, app_name.c_str(), &result);
1244 if (FAILED(hr) || result == FALSE)
1245 return ShellUtil::NOT_DEFAULT;
1248 return ShellUtil::IS_DEFAULT;
1251 // Probe the current commands registered to handle the shell "open" verb for
1252 // |protocols| (Windows XP); see ProbeProtocolHandlers.
1253 ShellUtil::DefaultState ProbeOpenCommandHandlers(
1254 const base::FilePath& chrome_exe,
1255 const wchar_t* const* protocols,
1256 size_t num_protocols) {
1257 // Get its short (8.3) form.
1258 base::string16 short_app_path;
1259 if (!ShortNameFromPath(chrome_exe, &short_app_path))
1260 return ShellUtil::UNKNOWN_DEFAULT;
1262 const HKEY root_key = HKEY_CLASSES_ROOT;
1263 base::string16 key_path;
1264 base::win::RegKey key;
1265 base::string16 value;
1266 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
1267 base::string16 short_path;
1269 for (size_t i = 0; i < num_protocols; ++i) {
1270 // Get the command line from HKCU\<protocol>\shell\open\command.
1271 key_path.assign(protocols[i]).append(ShellUtil::kRegShellOpen);
1272 if ((key.Open(root_key, key_path.c_str(),
1273 KEY_QUERY_VALUE) != ERROR_SUCCESS) ||
1274 (key.ReadValue(L"", &value) != ERROR_SUCCESS)) {
1275 return ShellUtil::NOT_DEFAULT;
1278 // Need to normalize path in case it's been munged.
1279 command_line = base::CommandLine::FromString(value);
1280 if (!ShortNameFromPath(command_line.GetProgram(), &short_path))
1281 return ShellUtil::UNKNOWN_DEFAULT;
1283 if (!base::FilePath::CompareEqualIgnoreCase(short_path, short_app_path))
1284 return ShellUtil::NOT_DEFAULT;
1287 return ShellUtil::IS_DEFAULT;
1290 // A helper function that probes default protocol handler registration (in a
1291 // manner appropriate for the current version of Windows) to determine if
1292 // Chrome is the default handler for |protocols|. Returns IS_DEFAULT
1293 // only if Chrome is the default for all specified protocols.
1294 ShellUtil::DefaultState ProbeProtocolHandlers(
1295 const base::FilePath& chrome_exe,
1296 const wchar_t* const* protocols,
1297 size_t num_protocols) {
1298 #if DCHECK_IS_ON()
1299 DCHECK(!num_protocols || protocols);
1300 for (size_t i = 0; i < num_protocols; ++i)
1301 DCHECK(protocols[i] && *protocols[i]);
1302 #endif
1304 const base::win::Version windows_version = base::win::GetVersion();
1306 if (windows_version >= base::win::VERSION_WIN8)
1307 return ProbeCurrentDefaultHandlers(chrome_exe, protocols, num_protocols);
1308 else if (windows_version >= base::win::VERSION_VISTA)
1309 return ProbeAppIsDefaultHandlers(chrome_exe, protocols, num_protocols);
1311 return ProbeOpenCommandHandlers(chrome_exe, protocols, num_protocols);
1314 // (Windows 8+) Finds and stores an app shortcuts folder path in *|path|.
1315 // Returns true on success.
1316 bool GetAppShortcutsFolder(BrowserDistribution* dist,
1317 ShellUtil::ShellChange level,
1318 base::FilePath *path) {
1319 DCHECK(path);
1320 DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8);
1322 base::FilePath folder;
1323 if (!PathService::Get(base::DIR_APP_SHORTCUTS, &folder)) {
1324 LOG(ERROR) << "Could not get application shortcuts location.";
1325 return false;
1328 folder = folder.Append(
1329 ShellUtil::GetBrowserModelId(dist, level == ShellUtil::CURRENT_USER));
1330 if (!base::DirectoryExists(folder)) {
1331 VLOG(1) << "No start screen shortcuts.";
1332 return false;
1335 *path = folder;
1336 return true;
1339 // Shortcut filters for BatchShortcutAction().
1341 typedef base::Callback<bool(const base::FilePath& /*shortcut_path*/,
1342 const base::string16& /*args*/)>
1343 ShortcutFilterCallback;
1345 // FilterTargetEq is a shortcut filter that matches only shortcuts that have a
1346 // specific target, and optionally matches shortcuts that have non-empty
1347 // arguments.
1348 class FilterTargetEq {
1349 public:
1350 FilterTargetEq(const base::FilePath& desired_target_exe, bool require_args);
1352 // Returns true if filter rules are satisfied, i.e.:
1353 // - |target_path|'s target == |desired_target_compare_|, and
1354 // - |args| is non-empty (if |require_args_| == true).
1355 bool Match(const base::FilePath& target_path,
1356 const base::string16& args) const;
1358 // A convenience routine to create a callback to call Match().
1359 // The callback is only valid during the lifetime of the FilterTargetEq
1360 // instance.
1361 ShortcutFilterCallback AsShortcutFilterCallback();
1363 private:
1364 InstallUtil::ProgramCompare desired_target_compare_;
1366 bool require_args_;
1369 FilterTargetEq::FilterTargetEq(const base::FilePath& desired_target_exe,
1370 bool require_args)
1371 : desired_target_compare_(desired_target_exe),
1372 require_args_(require_args) {}
1374 bool FilterTargetEq::Match(const base::FilePath& target_path,
1375 const base::string16& args) const {
1376 if (!desired_target_compare_.EvaluatePath(target_path))
1377 return false;
1378 if (require_args_ && args.empty())
1379 return false;
1380 return true;
1383 ShortcutFilterCallback FilterTargetEq::AsShortcutFilterCallback() {
1384 return base::Bind(&FilterTargetEq::Match, base::Unretained(this));
1387 // Shortcut operations for BatchShortcutAction().
1389 typedef base::Callback<bool(const base::FilePath& /*shortcut_path*/)>
1390 ShortcutOperationCallback;
1392 bool ShortcutOpUnpinFromTaskbar(const base::FilePath& shortcut_path) {
1393 VLOG(1) << "Trying to unpin from taskbar " << shortcut_path.value();
1394 if (!base::win::UnpinShortcutFromTaskbar(shortcut_path)) {
1395 VLOG(1) << shortcut_path.value()
1396 << " wasn't pinned to taskbar (or the unpin failed).";
1397 // No error, since shortcut might not be pinned.
1399 return true;
1402 bool ShortcutOpDelete(const base::FilePath& shortcut_path) {
1403 bool ret = base::DeleteFile(shortcut_path, false);
1404 LOG_IF(ERROR, !ret) << "Failed to remove " << shortcut_path.value();
1405 return ret;
1408 bool ShortcutOpRetarget(const base::FilePath& old_target,
1409 const base::FilePath& new_target,
1410 const base::FilePath& shortcut_path) {
1411 base::win::ShortcutProperties new_prop;
1412 new_prop.set_target(new_target);
1414 // If the old icon matches old target, then update icon while keeping the old
1415 // icon index. Non-fatal if we fail to get the old icon.
1416 base::win::ShortcutProperties old_prop;
1417 if (base::win::ResolveShortcutProperties(
1418 shortcut_path,
1419 base::win::ShortcutProperties::PROPERTIES_ICON,
1420 &old_prop)) {
1421 if (InstallUtil::ProgramCompare(old_target).EvaluatePath(old_prop.icon))
1422 new_prop.set_icon(new_target, old_prop.icon_index);
1423 } else {
1424 LOG(ERROR) << "Failed to resolve " << shortcut_path.value();
1427 bool result = base::win::CreateOrUpdateShortcutLink(
1428 shortcut_path, new_prop, base::win::SHORTCUT_UPDATE_EXISTING);
1429 LOG_IF(ERROR, !result) << "Failed to retarget " << shortcut_path.value();
1430 return result;
1433 bool ShortcutOpListOrRemoveUnknownArgs(
1434 bool do_removal,
1435 std::vector<std::pair<base::FilePath, base::string16> >* shortcuts,
1436 const base::FilePath& shortcut_path) {
1437 base::string16 args;
1438 if (!base::win::ResolveShortcut(shortcut_path, NULL, &args))
1439 return false;
1441 base::CommandLine current_args(base::CommandLine::FromString(
1442 base::StringPrintf(L"unused_program %ls", args.c_str())));
1443 const char* const kept_switches[] = {
1444 switches::kApp,
1445 switches::kAppId,
1446 switches::kShowAppList,
1447 switches::kProfileDirectory,
1449 base::CommandLine desired_args(base::CommandLine::NO_PROGRAM);
1450 desired_args.CopySwitchesFrom(current_args, kept_switches,
1451 arraysize(kept_switches));
1452 if (desired_args.argv().size() == current_args.argv().size())
1453 return true;
1454 if (shortcuts)
1455 shortcuts->push_back(std::make_pair(shortcut_path, args));
1456 if (!do_removal)
1457 return true;
1458 base::win::ShortcutProperties updated_properties;
1459 updated_properties.set_arguments(desired_args.GetArgumentsString());
1460 return base::win::CreateOrUpdateShortcutLink(
1461 shortcut_path, updated_properties, base::win::SHORTCUT_UPDATE_EXISTING);
1464 // {|location|, |dist|, |level|} determine |shortcut_folder|.
1465 // For each shortcut in |shortcut_folder| that match |shortcut_filter|, apply
1466 // |shortcut_operation|. Returns true if all operations are successful.
1467 // All intended operations are attempted, even if failures occur.
1468 // This method will abort and return false if |cancel| is non-NULL and gets set
1469 // at any point during this call.
1470 bool BatchShortcutAction(
1471 const ShortcutFilterCallback& shortcut_filter,
1472 const ShortcutOperationCallback& shortcut_operation,
1473 ShellUtil::ShortcutLocation location,
1474 BrowserDistribution* dist,
1475 ShellUtil::ShellChange level,
1476 const scoped_refptr<ShellUtil::SharedCancellationFlag>& cancel) {
1477 DCHECK(!shortcut_operation.is_null());
1479 // There is no system-level Quick Launch shortcut folder.
1480 if (level == ShellUtil::SYSTEM_LEVEL &&
1481 location == ShellUtil::SHORTCUT_LOCATION_QUICK_LAUNCH) {
1482 return true;
1485 base::FilePath shortcut_folder;
1486 if (!ShellUtil::GetShortcutPath(location, dist, level, &shortcut_folder)) {
1487 LOG(WARNING) << "Cannot find path at location " << location;
1488 return false;
1491 bool success = true;
1492 base::FileEnumerator enumerator(
1493 shortcut_folder, false, base::FileEnumerator::FILES,
1494 base::string16(L"*") + installer::kLnkExt);
1495 base::FilePath target_path;
1496 base::string16 args;
1497 for (base::FilePath shortcut_path = enumerator.Next();
1498 !shortcut_path.empty();
1499 shortcut_path = enumerator.Next()) {
1500 if (cancel.get() && cancel->data.IsSet())
1501 return false;
1502 if (base::win::ResolveShortcut(shortcut_path, &target_path, &args)) {
1503 if (shortcut_filter.Run(target_path, args) &&
1504 !shortcut_operation.Run(shortcut_path)) {
1505 success = false;
1507 } else {
1508 LOG(ERROR) << "Cannot resolve shortcut at " << shortcut_path.value();
1509 success = false;
1512 return success;
1516 // If the folder specified by {|location|, |dist|, |level|} is empty, remove it.
1517 // Otherwise do nothing. Returns true on success, including the vacuous case
1518 // where no deletion occurred because directory is non-empty.
1519 bool RemoveShortcutFolderIfEmpty(ShellUtil::ShortcutLocation location,
1520 BrowserDistribution* dist,
1521 ShellUtil::ShellChange level) {
1522 // Explicitly whitelist locations, since accidental calls can be very harmful.
1523 if (location != ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR &&
1524 location != ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR &&
1525 location != ShellUtil::SHORTCUT_LOCATION_APP_SHORTCUTS) {
1526 NOTREACHED();
1527 return false;
1530 base::FilePath shortcut_folder;
1531 if (!ShellUtil::GetShortcutPath(location, dist, level, &shortcut_folder)) {
1532 LOG(WARNING) << "Cannot find path at location " << location;
1533 return false;
1535 if (base::IsDirectoryEmpty(shortcut_folder) &&
1536 !base::DeleteFile(shortcut_folder, true)) {
1537 LOG(ERROR) << "Cannot remove folder " << shortcut_folder.value();
1538 return false;
1540 return true;
1543 } // namespace
1545 const wchar_t* ShellUtil::kRegDefaultIcon = L"\\DefaultIcon";
1546 const wchar_t* ShellUtil::kRegShellPath = L"\\shell";
1547 const wchar_t* ShellUtil::kRegShellOpen = L"\\shell\\open\\command";
1548 const wchar_t* ShellUtil::kRegStartMenuInternet =
1549 L"Software\\Clients\\StartMenuInternet";
1550 const wchar_t* ShellUtil::kRegClasses = L"Software\\Classes";
1551 const wchar_t* ShellUtil::kRegRegisteredApplications =
1552 L"Software\\RegisteredApplications";
1553 const wchar_t* ShellUtil::kRegVistaUrlPrefs =
1554 L"Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
1555 L"http\\UserChoice";
1556 const wchar_t* ShellUtil::kAppPathsRegistryKey =
1557 L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths";
1558 const wchar_t* ShellUtil::kAppPathsRegistryPathName = L"Path";
1560 const wchar_t* ShellUtil::kDefaultFileAssociations[] = {L".htm", L".html",
1561 L".shtml", L".xht", L".xhtml", NULL};
1562 const wchar_t* ShellUtil::kPotentialFileAssociations[] = {L".htm", L".html",
1563 L".shtml", L".xht", L".xhtml", L".webp", NULL};
1564 const wchar_t* ShellUtil::kBrowserProtocolAssociations[] = {L"ftp", L"http",
1565 L"https", NULL};
1566 const wchar_t* ShellUtil::kPotentialProtocolAssociations[] = {L"ftp", L"http",
1567 L"https", L"irc", L"mailto", L"mms", L"news", L"nntp", L"sms", L"smsto",
1568 L"tel", L"urn", L"webcal", NULL};
1569 const wchar_t* ShellUtil::kRegUrlProtocol = L"URL Protocol";
1570 const wchar_t* ShellUtil::kRegApplication = L"\\Application";
1571 const wchar_t* ShellUtil::kRegAppUserModelId = L"AppUserModelId";
1572 const wchar_t* ShellUtil::kRegApplicationDescription =
1573 L"ApplicationDescription";
1574 const wchar_t* ShellUtil::kRegApplicationName = L"ApplicationName";
1575 const wchar_t* ShellUtil::kRegApplicationIcon = L"ApplicationIcon";
1576 const wchar_t* ShellUtil::kRegApplicationCompany = L"ApplicationCompany";
1577 const wchar_t* ShellUtil::kRegExePath = L"\\.exe";
1578 const wchar_t* ShellUtil::kRegVerbOpen = L"open";
1579 const wchar_t* ShellUtil::kRegVerbOpenNewWindow = L"opennewwindow";
1580 const wchar_t* ShellUtil::kRegVerbRun = L"run";
1581 const wchar_t* ShellUtil::kRegCommand = L"command";
1582 const wchar_t* ShellUtil::kRegDelegateExecute = L"DelegateExecute";
1583 const wchar_t* ShellUtil::kRegOpenWithProgids = L"OpenWithProgids";
1585 ShellUtil::ShortcutProperties::ShortcutProperties(ShellChange level_in)
1586 : level(level_in),
1587 icon_index(0),
1588 dual_mode(false),
1589 pin_to_taskbar(false),
1590 pin_to_start(false),
1591 options(0U) {}
1593 ShellUtil::ShortcutProperties::~ShortcutProperties() {
1596 bool ShellUtil::QuickIsChromeRegisteredInHKLM(BrowserDistribution* dist,
1597 const base::FilePath& chrome_exe,
1598 const base::string16& suffix) {
1599 return QuickIsChromeRegistered(dist, chrome_exe, suffix,
1600 CONFIRM_SHELL_REGISTRATION_IN_HKLM);
1603 bool ShellUtil::ShortcutLocationIsSupported(
1604 ShellUtil::ShortcutLocation location) {
1605 switch (location) {
1606 case SHORTCUT_LOCATION_DESKTOP: // Falls through.
1607 case SHORTCUT_LOCATION_QUICK_LAUNCH: // Falls through.
1608 case SHORTCUT_LOCATION_START_MENU_ROOT: // Falls through.
1609 case SHORTCUT_LOCATION_START_MENU_CHROME_DIR: // Falls through.
1610 case SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR:
1611 return true;
1612 case SHORTCUT_LOCATION_TASKBAR_PINS:
1613 return base::win::GetVersion() >= base::win::VERSION_WIN7;
1614 case SHORTCUT_LOCATION_APP_SHORTCUTS:
1615 return base::win::GetVersion() >= base::win::VERSION_WIN8;
1616 default:
1617 NOTREACHED();
1618 return false;
1622 bool ShellUtil::GetShortcutPath(ShellUtil::ShortcutLocation location,
1623 BrowserDistribution* dist,
1624 ShellChange level,
1625 base::FilePath* path) {
1626 DCHECK(path);
1627 int dir_key = -1;
1628 base::string16 folder_to_append;
1629 switch (location) {
1630 case SHORTCUT_LOCATION_DESKTOP:
1631 dir_key = (level == CURRENT_USER) ? base::DIR_USER_DESKTOP :
1632 base::DIR_COMMON_DESKTOP;
1633 break;
1634 case SHORTCUT_LOCATION_QUICK_LAUNCH:
1635 // There is no support for a system-level Quick Launch shortcut.
1636 DCHECK_EQ(level, CURRENT_USER);
1637 dir_key = base::DIR_USER_QUICK_LAUNCH;
1638 break;
1639 case SHORTCUT_LOCATION_START_MENU_ROOT:
1640 dir_key = (level == CURRENT_USER) ? base::DIR_START_MENU :
1641 base::DIR_COMMON_START_MENU;
1642 break;
1643 case SHORTCUT_LOCATION_START_MENU_CHROME_DIR:
1644 dir_key = (level == CURRENT_USER) ? base::DIR_START_MENU :
1645 base::DIR_COMMON_START_MENU;
1646 folder_to_append = dist->GetStartMenuShortcutSubfolder(
1647 BrowserDistribution::SUBFOLDER_CHROME);
1648 break;
1649 case SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR:
1650 dir_key = (level == CURRENT_USER) ? base::DIR_START_MENU :
1651 base::DIR_COMMON_START_MENU;
1652 folder_to_append = dist->GetStartMenuShortcutSubfolder(
1653 BrowserDistribution::SUBFOLDER_APPS);
1654 break;
1655 case SHORTCUT_LOCATION_TASKBAR_PINS:
1656 dir_key = base::DIR_TASKBAR_PINS;
1657 break;
1658 case SHORTCUT_LOCATION_APP_SHORTCUTS:
1659 // TODO(huangs): Move GetAppShortcutsFolder() logic into base_paths_win.
1660 return GetAppShortcutsFolder(dist, level, path);
1662 default:
1663 NOTREACHED();
1664 return false;
1667 if (!PathService::Get(dir_key, path) || path->empty()) {
1668 NOTREACHED() << dir_key;
1669 return false;
1672 if (!folder_to_append.empty())
1673 *path = path->Append(folder_to_append);
1675 return true;
1678 bool ShellUtil::CreateOrUpdateShortcut(
1679 ShellUtil::ShortcutLocation location,
1680 BrowserDistribution* dist,
1681 const ShellUtil::ShortcutProperties& properties,
1682 ShellUtil::ShortcutOperation operation) {
1683 // Explicitly whitelist locations to which this is applicable.
1684 if (location != SHORTCUT_LOCATION_DESKTOP &&
1685 location != SHORTCUT_LOCATION_QUICK_LAUNCH &&
1686 location != SHORTCUT_LOCATION_START_MENU_ROOT &&
1687 location != SHORTCUT_LOCATION_START_MENU_CHROME_DIR &&
1688 location != SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR) {
1689 NOTREACHED();
1690 return false;
1693 DCHECK(dist);
1694 // |pin_to_taskbar| is only acknowledged when first creating the shortcut.
1695 DCHECK(!properties.pin_to_taskbar ||
1696 operation == SHELL_SHORTCUT_CREATE_ALWAYS ||
1697 operation == SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL);
1699 base::FilePath user_shortcut_path;
1700 base::FilePath system_shortcut_path;
1701 if (location == SHORTCUT_LOCATION_QUICK_LAUNCH) {
1702 // There is no system-level shortcut for Quick Launch.
1703 DCHECK_EQ(properties.level, CURRENT_USER);
1704 } else if (!GetShortcutPath(
1705 location, dist, SYSTEM_LEVEL, &system_shortcut_path)) {
1706 NOTREACHED();
1707 return false;
1710 base::string16 shortcut_name(
1711 ExtractShortcutNameFromProperties(dist, properties));
1712 system_shortcut_path = system_shortcut_path.Append(shortcut_name);
1714 base::FilePath* chosen_path;
1715 bool should_install_shortcut = true;
1716 if (properties.level == SYSTEM_LEVEL) {
1717 // Install the system-level shortcut if requested.
1718 chosen_path = &system_shortcut_path;
1719 } else if (operation != SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL ||
1720 system_shortcut_path.empty() ||
1721 !base::PathExists(system_shortcut_path)) {
1722 // Otherwise install the user-level shortcut, unless the system-level
1723 // variant of this shortcut is present on the machine and |operation| states
1724 // not to create a user-level shortcut in that case.
1725 if (!GetShortcutPath(location, dist, CURRENT_USER, &user_shortcut_path)) {
1726 NOTREACHED();
1727 return false;
1729 user_shortcut_path = user_shortcut_path.Append(shortcut_name);
1730 chosen_path = &user_shortcut_path;
1731 } else {
1732 // Do not install any shortcut if we are told to install a user-level
1733 // shortcut, but the system-level variant of that shortcut is present.
1734 // Other actions (e.g., pinning) can still happen with respect to the
1735 // existing system-level shortcut however.
1736 chosen_path = &system_shortcut_path;
1737 should_install_shortcut = false;
1740 if (chosen_path == NULL || chosen_path->empty()) {
1741 NOTREACHED();
1742 return false;
1745 base::win::ShortcutOperation shortcut_operation =
1746 TranslateShortcutOperation(operation);
1747 bool success = true;
1748 if (should_install_shortcut) {
1749 // Make sure the parent directories exist when creating the shortcut.
1750 if (shortcut_operation == base::win::SHORTCUT_CREATE_ALWAYS &&
1751 !base::CreateDirectory(chosen_path->DirName())) {
1752 NOTREACHED();
1753 return false;
1756 base::win::ShortcutProperties shortcut_properties(
1757 TranslateShortcutProperties(properties));
1758 success = base::win::CreateOrUpdateShortcutLink(
1759 *chosen_path, shortcut_properties, shortcut_operation);
1762 if (success && shortcut_operation == base::win::SHORTCUT_CREATE_ALWAYS) {
1763 if (properties.pin_to_taskbar &&
1764 base::win::GetVersion() >= base::win::VERSION_WIN7) {
1765 bool pinned = base::win::PinShortcutToTaskbar(*chosen_path);
1766 LOG_IF(ERROR, !pinned) << "Failed to pin to taskbar "
1767 << chosen_path->value();
1769 if (properties.pin_to_start &&
1770 base::win::GetVersion() >= base::win::VERSION_WIN10) {
1771 bool pinned = base::win::PinShortcutToStart(*chosen_path);
1772 LOG_IF(ERROR, !pinned) << "Failed to pin to start "
1773 << chosen_path->value();
1777 return success;
1780 base::string16 ShellUtil::FormatIconLocation(const base::FilePath& icon_path,
1781 int icon_index) {
1782 base::string16 icon_string(icon_path.value());
1783 icon_string.append(L",");
1784 icon_string.append(base::IntToString16(icon_index));
1785 return icon_string;
1788 base::string16 ShellUtil::GetChromeShellOpenCmd(
1789 const base::FilePath& chrome_exe) {
1790 return L"\"" + chrome_exe.value() + L"\" -- \"%1\"";
1793 base::string16 ShellUtil::GetChromeDelegateCommand(
1794 const base::FilePath& chrome_exe) {
1795 return L"\"" + chrome_exe.value() + L"\" -- %*";
1798 void ShellUtil::GetRegisteredBrowsers(
1799 BrowserDistribution* dist,
1800 std::map<base::string16, base::string16>* browsers) {
1801 DCHECK(dist);
1802 DCHECK(browsers);
1804 const base::string16 base_key(ShellUtil::kRegStartMenuInternet);
1805 base::string16 client_path;
1806 RegKey key;
1807 base::string16 name;
1808 base::string16 command;
1810 // HKCU has precedence over HKLM for these registrations: http://goo.gl/xjczJ.
1811 // Look in HKCU second to override any identical values found in HKLM.
1812 const HKEY roots[] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER };
1813 for (int i = 0; i < arraysize(roots); ++i) {
1814 const HKEY root = roots[i];
1815 for (base::win::RegistryKeyIterator iter(root, base_key.c_str());
1816 iter.Valid(); ++iter) {
1817 client_path.assign(base_key).append(1, L'\\').append(iter.Name());
1818 // Read the browser's name (localized according to install language).
1819 if (key.Open(root, client_path.c_str(),
1820 KEY_QUERY_VALUE) != ERROR_SUCCESS ||
1821 key.ReadValue(NULL, &name) != ERROR_SUCCESS ||
1822 name.empty() ||
1823 name.find(dist->GetBaseAppName()) != base::string16::npos) {
1824 continue;
1826 // Read the browser's reinstall command.
1827 if (key.Open(root, (client_path + L"\\InstallInfo").c_str(),
1828 KEY_QUERY_VALUE) == ERROR_SUCCESS &&
1829 key.ReadValue(kReinstallCommand, &command) == ERROR_SUCCESS &&
1830 !command.empty()) {
1831 (*browsers)[name] = command;
1837 base::string16 ShellUtil::GetCurrentInstallationSuffix(
1838 BrowserDistribution* dist,
1839 const base::FilePath& chrome_exe) {
1840 // This method is somewhat the opposite of GetInstallationSpecificSuffix().
1841 // In this case we are not trying to determine the current suffix for the
1842 // upcoming installation (i.e. not trying to stick to a currently bad
1843 // registration style if one is present).
1844 // Here we want to determine which suffix we should use at run-time.
1845 // In order of preference, we prefer (for user-level installs):
1846 // 1) Base 32 encoding of the md5 hash of the user's sid (new-style).
1847 // 2) Username (old-style).
1848 // 3) Unsuffixed (even worse).
1849 base::string16 tested_suffix;
1850 if (InstallUtil::IsPerUserInstall(chrome_exe) &&
1851 (!GetUserSpecificRegistrySuffix(&tested_suffix) ||
1852 !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix,
1853 CONFIRM_PROGID_REGISTRATION)) &&
1854 (!GetOldUserSpecificRegistrySuffix(&tested_suffix) ||
1855 !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix,
1856 CONFIRM_PROGID_REGISTRATION)) &&
1857 !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix.erase(),
1858 CONFIRM_PROGID_REGISTRATION)) {
1859 // If Chrome is not registered under any of the possible suffixes (e.g.
1860 // tests, Canary, etc.): use the new-style suffix at run-time.
1861 if (!GetUserSpecificRegistrySuffix(&tested_suffix))
1862 NOTREACHED();
1864 return tested_suffix;
1867 base::string16 ShellUtil::GetApplicationName(BrowserDistribution* dist,
1868 const base::FilePath& chrome_exe) {
1869 base::string16 app_name = dist->GetBaseAppName();
1870 app_name += GetCurrentInstallationSuffix(dist, chrome_exe);
1871 return app_name;
1874 base::string16 ShellUtil::GetBrowserModelId(BrowserDistribution* dist,
1875 bool is_per_user_install) {
1876 base::string16 app_id(dist->GetBaseAppId());
1877 base::string16 suffix;
1879 // TODO(robertshield): Temporary hack to make the kRegisterChromeBrowserSuffix
1880 // apply to all registry values computed down in these murky depths.
1881 base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
1882 if (command_line.HasSwitch(
1883 installer::switches::kRegisterChromeBrowserSuffix)) {
1884 suffix = command_line.GetSwitchValueNative(
1885 installer::switches::kRegisterChromeBrowserSuffix);
1886 } else if (is_per_user_install && !GetUserSpecificRegistrySuffix(&suffix)) {
1887 NOTREACHED();
1889 // There is only one component (i.e. the suffixed appid) in this case, but it
1890 // is still necessary to go through the appid constructor to make sure the
1891 // returned appid is truncated if necessary.
1892 std::vector<base::string16> components(1, app_id.append(suffix));
1893 return BuildAppModelId(components);
1896 base::string16 ShellUtil::BuildAppModelId(
1897 const std::vector<base::string16>& components) {
1898 DCHECK_GT(components.size(), 0U);
1900 // Find the maximum numbers of characters allowed in each component
1901 // (accounting for the dots added between each component).
1902 const size_t available_chars =
1903 installer::kMaxAppModelIdLength - (components.size() - 1);
1904 const size_t max_component_length = available_chars / components.size();
1906 // |max_component_length| should be at least 2; otherwise the truncation logic
1907 // below breaks.
1908 if (max_component_length < 2U) {
1909 NOTREACHED();
1910 return (*components.begin()).substr(0, installer::kMaxAppModelIdLength);
1913 base::string16 app_id;
1914 app_id.reserve(installer::kMaxAppModelIdLength);
1915 for (std::vector<base::string16>::const_iterator it = components.begin();
1916 it != components.end(); ++it) {
1917 if (it != components.begin())
1918 app_id.push_back(L'.');
1920 const base::string16& component = *it;
1921 DCHECK(!component.empty());
1922 if (component.length() > max_component_length) {
1923 // Append a shortened version of this component. Cut in the middle to try
1924 // to avoid losing the unique parts of this component (which are usually
1925 // at the beginning or end for things like usernames and paths).
1926 app_id.append(component.c_str(), 0, max_component_length / 2);
1927 app_id.append(component.c_str(),
1928 component.length() - ((max_component_length + 1) / 2),
1929 base::string16::npos);
1930 } else {
1931 app_id.append(component);
1934 // No spaces are allowed in the AppUserModelId according to MSDN.
1935 base::ReplaceChars(app_id, base::ASCIIToUTF16(" "), base::ASCIIToUTF16("_"),
1936 &app_id);
1937 return app_id;
1940 ShellUtil::DefaultState ShellUtil::GetChromeDefaultState() {
1941 base::FilePath app_path;
1942 if (!PathService::Get(base::FILE_EXE, &app_path)) {
1943 NOTREACHED();
1944 return ShellUtil::UNKNOWN_DEFAULT;
1947 return GetChromeDefaultStateFromPath(app_path);
1950 ShellUtil::DefaultState ShellUtil::GetChromeDefaultStateFromPath(
1951 const base::FilePath& chrome_exe) {
1952 BrowserDistribution* distribution = BrowserDistribution::GetDistribution();
1953 if (distribution->GetDefaultBrowserControlPolicy() ==
1954 BrowserDistribution::DEFAULT_BROWSER_UNSUPPORTED) {
1955 return NOT_DEFAULT;
1957 // When we check for default browser we don't necessarily want to count file
1958 // type handlers and icons as having changed the default browser status,
1959 // since the user may have changed their shell settings to cause HTML files
1960 // to open with a text editor for example. We also don't want to aggressively
1961 // claim FTP, since the user may have a separate FTP client. It is an open
1962 // question as to how to "heal" these settings. Perhaps the user should just
1963 // re-run the installer or run with the --set-default-browser command line
1964 // flag. There is doubtless some other key we can hook into to cause "Repair"
1965 // to show up in Add/Remove programs for us.
1966 static const wchar_t* const kChromeProtocols[] = { L"http", L"https" };
1967 DefaultState default_state = ProbeProtocolHandlers(
1968 chrome_exe, kChromeProtocols, arraysize(kChromeProtocols));
1969 UpdateDefaultBrowserBeaconWithState(chrome_exe, distribution, default_state);
1970 return default_state;
1973 ShellUtil::DefaultState ShellUtil::GetChromeDefaultProtocolClientState(
1974 const base::string16& protocol) {
1975 BrowserDistribution* distribution = BrowserDistribution::GetDistribution();
1976 if (distribution->GetDefaultBrowserControlPolicy() ==
1977 BrowserDistribution::DEFAULT_BROWSER_UNSUPPORTED) {
1978 return NOT_DEFAULT;
1981 if (protocol.empty())
1982 return UNKNOWN_DEFAULT;
1984 base::FilePath chrome_exe;
1985 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
1986 NOTREACHED();
1987 return ShellUtil::UNKNOWN_DEFAULT;
1990 const wchar_t* const protocols[] = { protocol.c_str() };
1991 return ProbeProtocolHandlers(chrome_exe,
1992 protocols,
1993 arraysize(protocols));
1996 // static
1997 bool ShellUtil::CanMakeChromeDefaultUnattended() {
1998 return base::win::GetVersion() < base::win::VERSION_WIN8;
2001 bool ShellUtil::MakeChromeDefault(BrowserDistribution* dist,
2002 int shell_change,
2003 const base::FilePath& chrome_exe,
2004 bool elevate_if_not_admin) {
2005 DCHECK(!(shell_change & ShellUtil::SYSTEM_LEVEL) || IsUserAnAdmin());
2007 BrowserDistribution* distribution = BrowserDistribution::GetDistribution();
2008 if (distribution->GetDefaultBrowserControlPolicy() !=
2009 BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL) {
2010 return false;
2013 // Windows 8 does not permit making a browser default just like that.
2014 // This process needs to be routed through the system's UI. Use
2015 // ShowMakeChromeDefaultSystemUI instead (below).
2016 if (!CanMakeChromeDefaultUnattended()) {
2017 return false;
2020 if (!ShellUtil::RegisterChromeBrowser(
2021 dist, chrome_exe, base::string16(), elevate_if_not_admin)) {
2022 return false;
2025 bool ret = true;
2026 // First use the new "recommended" way on Vista to make Chrome default
2027 // browser.
2028 base::string16 app_name = GetApplicationName(dist, chrome_exe);
2030 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
2031 // On Windows Vista and Win7 we still can set ourselves via the
2032 // the IApplicationAssociationRegistration interface.
2033 VLOG(1) << "Registering Chrome as default browser on Vista.";
2034 base::win::ScopedComPtr<IApplicationAssociationRegistration> pAAR;
2035 HRESULT hr = pAAR.CreateInstance(CLSID_ApplicationAssociationRegistration,
2036 NULL, CLSCTX_INPROC);
2037 if (SUCCEEDED(hr)) {
2038 for (int i = 0; ShellUtil::kBrowserProtocolAssociations[i] != NULL; i++) {
2039 hr = pAAR->SetAppAsDefault(app_name.c_str(),
2040 ShellUtil::kBrowserProtocolAssociations[i], AT_URLPROTOCOL);
2041 if (!SUCCEEDED(hr)) {
2042 ret = false;
2043 LOG(ERROR) << "Failed to register as default for protocol "
2044 << ShellUtil::kBrowserProtocolAssociations[i]
2045 << " (" << hr << ")";
2049 for (int i = 0; ShellUtil::kDefaultFileAssociations[i] != NULL; i++) {
2050 hr = pAAR->SetAppAsDefault(app_name.c_str(),
2051 ShellUtil::kDefaultFileAssociations[i], AT_FILEEXTENSION);
2052 if (!SUCCEEDED(hr)) {
2053 ret = false;
2054 LOG(ERROR) << "Failed to register as default for file extension "
2055 << ShellUtil::kDefaultFileAssociations[i]
2056 << " (" << hr << ")";
2062 if (!RegisterChromeAsDefaultXPStyle(dist, shell_change, chrome_exe))
2063 ret = false;
2065 // Send Windows notification event so that it can update icons for
2066 // file associations.
2067 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
2068 return ret;
2071 bool ShellUtil::ShowMakeChromeDefaultSystemUI(
2072 BrowserDistribution* dist,
2073 const base::FilePath& chrome_exe) {
2074 DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8);
2075 if (dist->GetDefaultBrowserControlPolicy() !=
2076 BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL) {
2077 return false;
2080 if (!RegisterChromeBrowser(dist, chrome_exe, base::string16(), true))
2081 return false;
2083 bool succeeded = true;
2084 bool is_default = (GetChromeDefaultState() == IS_DEFAULT);
2085 if (!is_default) {
2086 if (base::win::GetVersion() < base::win::VERSION_WIN10) {
2087 // On Windows 8, you can't set yourself as the default handler
2088 // programatically. In other words IApplicationAssociationRegistration
2089 // has been rendered useless. What you can do is to launch
2090 // "Set Program Associations" section of the "Default Programs"
2091 // control panel, which is a mess, or pop the concise "How you want to
2092 // open webpages?" dialog. We choose the latter.
2093 succeeded = LaunchSelectDefaultProtocolHandlerDialog(L"http");
2094 } else {
2095 // On Windows 10, you can't even launch the associations dialog.
2096 // So we launch the settings dialog. Quoting from MSDN: "The Open With
2097 // dialog box can no longer be used to change the default program used to
2098 // open a file extension. You can only use SHOpenWithDialog to open
2099 // a single file."
2100 succeeded = LaunchDefaultAppsSettingsModernDialog();
2102 is_default = (succeeded && GetChromeDefaultState() == IS_DEFAULT);
2104 if (succeeded && is_default)
2105 RegisterChromeAsDefaultXPStyle(dist, CURRENT_USER, chrome_exe);
2106 return succeeded;
2109 bool ShellUtil::MakeChromeDefaultProtocolClient(
2110 BrowserDistribution* dist,
2111 const base::FilePath& chrome_exe,
2112 const base::string16& protocol) {
2113 if (dist->GetDefaultBrowserControlPolicy() !=
2114 BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL) {
2115 return false;
2118 if (!RegisterChromeForProtocol(
2119 dist, chrome_exe, base::string16(), protocol, true))
2120 return false;
2122 // Windows 8 does not permit making a browser default just like that.
2123 // This process needs to be routed through the system's UI. Use
2124 // ShowMakeChromeDefaultProocolClientSystemUI instead (below).
2125 if (!CanMakeChromeDefaultUnattended())
2126 return false;
2128 bool ret = true;
2129 // First use the new "recommended" way on Vista to make Chrome default
2130 // protocol handler.
2131 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
2132 VLOG(1) << "Registering Chrome as default handler for " << protocol
2133 << " on Vista.";
2134 base::win::ScopedComPtr<IApplicationAssociationRegistration> pAAR;
2135 HRESULT hr = pAAR.CreateInstance(CLSID_ApplicationAssociationRegistration,
2136 NULL, CLSCTX_INPROC);
2137 if (SUCCEEDED(hr)) {
2138 base::string16 app_name = GetApplicationName(dist, chrome_exe);
2139 hr = pAAR->SetAppAsDefault(app_name.c_str(), protocol.c_str(),
2140 AT_URLPROTOCOL);
2142 if (!SUCCEEDED(hr)) {
2143 ret = false;
2144 LOG(ERROR) << "Could not make Chrome default protocol client (Vista):"
2145 << " HRESULT=" << hr << ".";
2149 // Now use the old way to associate Chrome with the desired protocol. This
2150 // should not be required on Vista+, but since some applications still read
2151 // Software\Classes\<protocol> key directly, do this on Vista+ also.
2152 if (!RegisterChromeAsDefaultProtocolClientXPStyle(dist, chrome_exe, protocol))
2153 ret = false;
2155 return ret;
2158 bool ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(
2159 BrowserDistribution* dist,
2160 const base::FilePath& chrome_exe,
2161 const base::string16& protocol) {
2162 DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8);
2163 if (dist->GetDefaultBrowserControlPolicy() !=
2164 BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL) {
2165 return false;
2168 if (!RegisterChromeForProtocol(
2169 dist, chrome_exe, base::string16(), protocol, true))
2170 return false;
2172 bool succeeded = true;
2173 bool is_default = (
2174 GetChromeDefaultProtocolClientState(protocol) == IS_DEFAULT);
2175 if (!is_default) {
2176 // On Windows 8, you can't set yourself as the default handler
2177 // programatically. In other words IApplicationAssociationRegistration
2178 // has been rendered useless. What you can do is to launch
2179 // "Set Program Associations" section of the "Default Programs"
2180 // control panel, which is a mess, or pop the concise "How you want to open
2181 // links of this type (protocol)?" dialog. We choose the latter.
2182 succeeded = LaunchSelectDefaultProtocolHandlerDialog(protocol.c_str());
2183 is_default = (succeeded &&
2184 GetChromeDefaultProtocolClientState(protocol) == IS_DEFAULT);
2186 if (succeeded && is_default)
2187 RegisterChromeAsDefaultProtocolClientXPStyle(dist, chrome_exe, protocol);
2188 return succeeded;
2191 bool ShellUtil::RegisterChromeBrowser(BrowserDistribution* dist,
2192 const base::FilePath& chrome_exe,
2193 const base::string16& unique_suffix,
2194 bool elevate_if_not_admin) {
2195 if (dist->GetDefaultBrowserControlPolicy() ==
2196 BrowserDistribution::DEFAULT_BROWSER_UNSUPPORTED) {
2197 return false;
2200 base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
2202 base::string16 suffix;
2203 if (!unique_suffix.empty()) {
2204 suffix = unique_suffix;
2205 } else if (command_line.HasSwitch(
2206 installer::switches::kRegisterChromeBrowserSuffix)) {
2207 suffix = command_line.GetSwitchValueNative(
2208 installer::switches::kRegisterChromeBrowserSuffix);
2209 } else if (!GetInstallationSpecificSuffix(dist, chrome_exe, &suffix)) {
2210 return false;
2213 RemoveRunVerbOnWindows8(dist, chrome_exe);
2215 bool user_level = InstallUtil::IsPerUserInstall(chrome_exe);
2216 HKEY root = DetermineRegistrationRoot(user_level);
2218 // Look only in HKLM for system-level installs (otherwise, if a user-level
2219 // install is also present, it will lead IsChromeRegistered() to think this
2220 // system-level install isn't registered properly as it is shadowed by the
2221 // user-level install's registrations).
2222 uint32 look_for_in = user_level ?
2223 RegistryEntry::LOOK_IN_HKCU_THEN_HKLM : RegistryEntry::LOOK_IN_HKLM;
2225 // Check if chrome is already registered with this suffix.
2226 if (IsChromeRegistered(dist, chrome_exe, suffix, look_for_in))
2227 return true;
2229 bool result = true;
2230 if (root == HKEY_CURRENT_USER || IsUserAnAdmin()) {
2231 // Do the full registration if we can do it at user-level or if the user is
2232 // an admin.
2233 ScopedVector<RegistryEntry> progid_and_appreg_entries;
2234 ScopedVector<RegistryEntry> shell_entries;
2235 RegistryEntry::GetChromeProgIdEntries(
2236 dist, chrome_exe, suffix, &progid_and_appreg_entries);
2237 RegistryEntry::GetChromeAppRegistrationEntries(
2238 chrome_exe, suffix, &progid_and_appreg_entries);
2239 RegistryEntry::GetShellIntegrationEntries(
2240 dist, chrome_exe, suffix, &shell_entries);
2241 result = (AddRegistryEntries(root, progid_and_appreg_entries) &&
2242 AddRegistryEntries(root, shell_entries));
2243 } else if (elevate_if_not_admin &&
2244 base::win::GetVersion() >= base::win::VERSION_VISTA &&
2245 ElevateAndRegisterChrome(dist, chrome_exe, suffix, base::string16())) {
2246 // If the user is not an admin and OS is between Vista and Windows 7
2247 // inclusively, try to elevate and register. This is only intended for
2248 // user-level installs as system-level installs should always be run with
2249 // admin rights.
2250 result = true;
2251 } else {
2252 // If we got to this point then all we can do is create ProgId and basic app
2253 // registrations under HKCU.
2254 ScopedVector<RegistryEntry> entries;
2255 RegistryEntry::GetChromeProgIdEntries(
2256 dist, chrome_exe, base::string16(), &entries);
2257 // Prefer to use |suffix|; unless Chrome's ProgIds are already registered
2258 // with no suffix (as per the old registration style): in which case some
2259 // other registry entries could refer to them and since we were not able to
2260 // set our HKLM entries above, we are better off not altering these here.
2261 if (!AreEntriesRegistered(entries, RegistryEntry::LOOK_IN_HKCU)) {
2262 if (!suffix.empty()) {
2263 entries.clear();
2264 RegistryEntry::GetChromeProgIdEntries(
2265 dist, chrome_exe, suffix, &entries);
2266 RegistryEntry::GetChromeAppRegistrationEntries(
2267 chrome_exe, suffix, &entries);
2269 result = AddRegistryEntries(HKEY_CURRENT_USER, entries);
2270 } else {
2271 // The ProgId is registered unsuffixed in HKCU, also register the app with
2272 // Windows in HKCU (this was not done in the old registration style and
2273 // thus needs to be done after the above check for the unsuffixed
2274 // registration).
2275 entries.clear();
2276 RegistryEntry::GetChromeAppRegistrationEntries(
2277 chrome_exe, base::string16(), &entries);
2278 result = AddRegistryEntries(HKEY_CURRENT_USER, entries);
2281 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
2282 return result;
2285 bool ShellUtil::RegisterChromeForProtocol(BrowserDistribution* dist,
2286 const base::FilePath& chrome_exe,
2287 const base::string16& unique_suffix,
2288 const base::string16& protocol,
2289 bool elevate_if_not_admin) {
2290 if (dist->GetDefaultBrowserControlPolicy() ==
2291 BrowserDistribution::DEFAULT_BROWSER_UNSUPPORTED) {
2292 return false;
2295 base::string16 suffix;
2296 if (!unique_suffix.empty()) {
2297 suffix = unique_suffix;
2298 } else if (!GetInstallationSpecificSuffix(dist, chrome_exe, &suffix)) {
2299 return false;
2302 bool user_level = InstallUtil::IsPerUserInstall(chrome_exe);
2303 HKEY root = DetermineRegistrationRoot(user_level);
2305 // Look only in HKLM for system-level installs (otherwise, if a user-level
2306 // install is also present, it could lead IsChromeRegisteredForProtocol() to
2307 // think this system-level install isn't registered properly as it may be
2308 // shadowed by the user-level install's registrations).
2309 uint32 look_for_in = user_level ?
2310 RegistryEntry::LOOK_IN_HKCU_THEN_HKLM : RegistryEntry::LOOK_IN_HKLM;
2312 // Check if chrome is already registered with this suffix.
2313 if (IsChromeRegisteredForProtocol(dist, suffix, protocol, look_for_in))
2314 return true;
2316 if (root == HKEY_CURRENT_USER || IsUserAnAdmin()) {
2317 // We can do this operation directly.
2318 // First, make sure Chrome is fully registered on this machine.
2319 if (!RegisterChromeBrowser(dist, chrome_exe, suffix, false))
2320 return false;
2322 // Write in the capabillity for the protocol.
2323 ScopedVector<RegistryEntry> entries;
2324 RegistryEntry::GetProtocolCapabilityEntries(dist, suffix, protocol,
2325 &entries);
2326 return AddRegistryEntries(root, entries);
2327 } else if (elevate_if_not_admin &&
2328 base::win::GetVersion() >= base::win::VERSION_VISTA) {
2329 // Elevate to do the whole job
2330 return ElevateAndRegisterChrome(dist, chrome_exe, suffix, protocol);
2331 } else {
2332 // Admin rights are required to register capabilities before Windows 8.
2333 return false;
2337 // static
2338 bool ShellUtil::RemoveShortcuts(ShellUtil::ShortcutLocation location,
2339 BrowserDistribution* dist,
2340 ShellChange level,
2341 const base::FilePath& target_exe) {
2342 if (!ShellUtil::ShortcutLocationIsSupported(location))
2343 return true; // Vacuous success.
2345 FilterTargetEq shortcut_filter(target_exe, false);
2346 // Main operation to apply to each shortcut in the directory specified.
2347 ShortcutOperationCallback shortcut_operation(
2348 location == SHORTCUT_LOCATION_TASKBAR_PINS
2349 ? base::Bind(&ShortcutOpUnpinFromTaskbar)
2350 : base::Bind(&ShortcutOpDelete));
2351 bool success = BatchShortcutAction(shortcut_filter.AsShortcutFilterCallback(),
2352 shortcut_operation, location, dist, level,
2353 NULL);
2354 // Remove chrome-specific shortcut folders if they are now empty.
2355 if (success &&
2356 (location == SHORTCUT_LOCATION_START_MENU_CHROME_DIR ||
2357 location == SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR ||
2358 location == SHORTCUT_LOCATION_APP_SHORTCUTS)) {
2359 success = RemoveShortcutFolderIfEmpty(location, dist, level);
2361 return success;
2364 // static
2365 bool ShellUtil::RetargetShortcutsWithArgs(
2366 ShellUtil::ShortcutLocation location,
2367 BrowserDistribution* dist,
2368 ShellChange level,
2369 const base::FilePath& old_target_exe,
2370 const base::FilePath& new_target_exe) {
2371 if (!ShellUtil::ShortcutLocationIsSupported(location))
2372 return true; // Vacuous success.
2374 FilterTargetEq shortcut_filter(old_target_exe, true);
2375 ShortcutOperationCallback shortcut_operation(
2376 base::Bind(&ShortcutOpRetarget, old_target_exe, new_target_exe));
2377 return BatchShortcutAction(shortcut_filter.AsShortcutFilterCallback(),
2378 shortcut_operation, location, dist, level, NULL);
2381 // static
2382 bool ShellUtil::ShortcutListMaybeRemoveUnknownArgs(
2383 ShellUtil::ShortcutLocation location,
2384 BrowserDistribution* dist,
2385 ShellChange level,
2386 const base::FilePath& chrome_exe,
2387 bool do_removal,
2388 const scoped_refptr<SharedCancellationFlag>& cancel,
2389 std::vector<std::pair<base::FilePath, base::string16> >* shortcuts) {
2390 if (!ShellUtil::ShortcutLocationIsSupported(location))
2391 return false;
2392 DCHECK(dist);
2393 FilterTargetEq shortcut_filter(chrome_exe, true);
2394 ShortcutOperationCallback shortcut_operation(
2395 base::Bind(&ShortcutOpListOrRemoveUnknownArgs, do_removal, shortcuts));
2396 return BatchShortcutAction(shortcut_filter.AsShortcutFilterCallback(),
2397 shortcut_operation, location, dist, level, cancel);
2400 bool ShellUtil::GetUserSpecificRegistrySuffix(base::string16* suffix) {
2401 // Use a thread-safe cache for the user's suffix.
2402 static base::LazyInstance<UserSpecificRegistrySuffix>::Leaky suffix_instance =
2403 LAZY_INSTANCE_INITIALIZER;
2404 return suffix_instance.Get().GetSuffix(suffix);
2407 bool ShellUtil::GetOldUserSpecificRegistrySuffix(base::string16* suffix) {
2408 wchar_t user_name[256];
2409 DWORD size = arraysize(user_name);
2410 if (::GetUserName(user_name, &size) == 0 || size < 1) {
2411 NOTREACHED();
2412 return false;
2414 suffix->reserve(size);
2415 suffix->assign(1, L'.');
2416 suffix->append(user_name, size - 1);
2417 return true;
2420 base::string16 ShellUtil::ByteArrayToBase32(const uint8* bytes, size_t size) {
2421 static const char kEncoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
2423 // Eliminate special cases first.
2424 if (size == 0) {
2425 return base::string16();
2426 } else if (size == 1) {
2427 base::string16 ret;
2428 ret.push_back(kEncoding[(bytes[0] & 0xf8) >> 3]);
2429 ret.push_back(kEncoding[(bytes[0] & 0x07) << 2]);
2430 return ret;
2431 } else if (size >= std::numeric_limits<size_t>::max() / 8) {
2432 // If |size| is too big, the calculation of |encoded_length| below will
2433 // overflow.
2434 NOTREACHED();
2435 return base::string16();
2438 // Overestimate the number of bits in the string by 4 so that dividing by 5
2439 // is the equivalent of rounding up the actual number of bits divided by 5.
2440 const size_t encoded_length = (size * 8 + 4) / 5;
2442 base::string16 ret;
2443 ret.reserve(encoded_length);
2445 // A bit stream which will be read from the left and appended to from the
2446 // right as it's emptied.
2447 uint16 bit_stream = (bytes[0] << 8) + bytes[1];
2448 size_t next_byte_index = 2;
2449 int free_bits = 0;
2450 while (free_bits < 16) {
2451 // Extract the 5 leftmost bits in the stream
2452 ret.push_back(kEncoding[(bit_stream & 0xf800) >> 11]);
2453 bit_stream <<= 5;
2454 free_bits += 5;
2456 // If there is enough room in the bit stream, inject another byte (if there
2457 // are any left...).
2458 if (free_bits >= 8 && next_byte_index < size) {
2459 free_bits -= 8;
2460 bit_stream += bytes[next_byte_index++] << free_bits;
2464 DCHECK_EQ(ret.length(), encoded_length);
2465 return ret;
2468 // static
2469 bool ShellUtil::AddFileAssociations(
2470 const base::string16& prog_id,
2471 const base::CommandLine& command_line,
2472 const base::string16& file_type_name,
2473 const base::FilePath& icon_path,
2474 const std::set<base::string16>& file_extensions) {
2475 ScopedVector<RegistryEntry> entries;
2477 // Create a class for this app.
2478 RegistryEntry::ApplicationInfo app_info;
2479 app_info.prog_id = prog_id;
2480 app_info.file_type_name = file_type_name;
2481 app_info.file_type_icon_path = icon_path;
2482 app_info.file_type_icon_index = 0;
2483 app_info.command_line = command_line.GetCommandLineStringWithPlaceholders();
2484 RegistryEntry::GetProgIdEntries(app_info, &entries);
2486 // Associate each extension that the app can handle with the class. Set this
2487 // app as the default handler if and only if there is no existing default.
2488 for (std::set<base::string16>::const_iterator it = file_extensions.begin();
2489 it != file_extensions.end();
2490 ++it) {
2491 // Do not allow empty file extensions, or extensions beginning with a '.'.
2492 DCHECK(!it->empty());
2493 DCHECK_NE(L'.', (*it)[0]);
2494 base::string16 ext(1, L'.');
2495 ext.append(*it);
2496 RegistryEntry::GetAppExtRegistrationEntries(prog_id, ext, &entries);
2498 // Regstering as the default will have no effect on Windows 8 (see
2499 // documentation for GetAppDefaultRegistrationEntries). However, if our app
2500 // is the only handler, it will automatically become the default, so the
2501 // same effect is achieved.
2502 RegistryEntry::GetAppDefaultRegistrationEntries(
2503 prog_id, ext, false, &entries);
2506 return AddRegistryEntries(HKEY_CURRENT_USER, entries);
2509 // static
2510 bool ShellUtil::DeleteFileAssociations(const base::string16& prog_id) {
2511 // Delete the key HKEY_CURRENT_USER\Software\Classes\PROGID.
2512 base::string16 key_path(ShellUtil::kRegClasses);
2513 key_path.push_back(base::FilePath::kSeparators[0]);
2514 key_path.append(prog_id);
2515 return InstallUtil::DeleteRegistryKey(
2516 HKEY_CURRENT_USER, key_path, WorkItem::kWow64Default);
2518 // TODO(mgiuca): Remove the extension association entries. This requires that
2519 // the extensions associated with a particular prog_id are stored in that
2520 // prog_id's key.