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.
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
10 #include "chrome/installer/util/shell_util.h"
18 #include "base/bind.h"
19 #include "base/command_line.h"
20 #include "base/files/file_enumerator.h"
21 #include "base/files/file_path.h"
22 #include "base/files/file_util.h"
23 #include "base/lazy_instance.h"
24 #include "base/logging.h"
26 #include "base/memory/scoped_ptr.h"
27 #include "base/memory/scoped_vector.h"
28 #include "base/path_service.h"
29 #include "base/strings/string16.h"
30 #include "base/strings/string_number_conversions.h"
31 #include "base/strings/string_split.h"
32 #include "base/strings/string_util.h"
33 #include "base/strings/stringprintf.h"
34 #include "base/strings/utf_string_conversions.h"
35 #include "base/synchronization/cancellation_flag.h"
36 #include "base/values.h"
37 #include "base/win/registry.h"
38 #include "base/win/scoped_co_mem.h"
39 #include "base/win/scoped_comptr.h"
40 #include "base/win/shortcut.h"
41 #include "base/win/win_util.h"
42 #include "base/win/windows_version.h"
43 #include "chrome/common/chrome_constants.h"
44 #include "chrome/common/chrome_switches.h"
45 #include "chrome/installer/util/browser_distribution.h"
46 #include "chrome/installer/util/install_util.h"
47 #include "chrome/installer/util/l10n_string_util.h"
48 #include "chrome/installer/util/master_preferences.h"
49 #include "chrome/installer/util/master_preferences_constants.h"
50 #include "chrome/installer/util/util_constants.h"
51 #include "chrome/installer/util/work_item.h"
53 #include "installer_util_strings.h" // NOLINT
55 using base::win::RegKey
;
59 // An enum used to tell QuickIsChromeRegistered() which level of registration
60 // the caller wants to confirm.
61 enum RegistrationConfirmationLevel
{
62 // Only look for Chrome's ProgIds.
63 // This is sufficient when we are trying to determine the suffix of the
64 // currently running Chrome as shell integration registrations might not be
66 CONFIRM_PROGID_REGISTRATION
= 0,
67 // Confirm that Chrome is fully integrated with Windows (i.e. registered with
68 // Defaut Programs). These registrations can be in HKCU as of Windows 8.
69 // Note: Shell registration implies ProgId registration.
70 CONFIRM_SHELL_REGISTRATION
,
71 // Same as CONFIRM_SHELL_REGISTRATION, but only look in HKLM (used when
72 // uninstalling to know whether elevation is required to clean up the
74 CONFIRM_SHELL_REGISTRATION_IN_HKLM
,
77 const wchar_t kReinstallCommand
[] = L
"ReinstallCommand";
79 // Returns true if Chrome Metro is supported on this OS (Win 8 8370 or greater).
80 // TODO(gab): Change this to a simple check for Win 8 once old Win8 builds
82 bool IsChromeMetroSupported() {
83 OSVERSIONINFOEX min_version_info
= {};
84 min_version_info
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOEX
);
85 min_version_info
.dwMajorVersion
= 6;
86 min_version_info
.dwMinorVersion
= 2;
87 min_version_info
.dwBuildNumber
= 8370;
88 min_version_info
.wServicePackMajor
= 0;
89 min_version_info
.wServicePackMinor
= 0;
91 DWORDLONG condition_mask
= 0;
92 VER_SET_CONDITION(condition_mask
, VER_MAJORVERSION
, VER_GREATER_EQUAL
);
93 VER_SET_CONDITION(condition_mask
, VER_MINORVERSION
, VER_GREATER_EQUAL
);
94 VER_SET_CONDITION(condition_mask
, VER_BUILDNUMBER
, VER_GREATER_EQUAL
);
95 VER_SET_CONDITION(condition_mask
, VER_SERVICEPACKMAJOR
, VER_GREATER_EQUAL
);
96 VER_SET_CONDITION(condition_mask
, VER_SERVICEPACKMINOR
, VER_GREATER_EQUAL
);
98 DWORD type_mask
= VER_MAJORVERSION
| VER_MINORVERSION
| VER_BUILDNUMBER
|
99 VER_SERVICEPACKMAJOR
| VER_SERVICEPACKMINOR
;
101 return VerifyVersionInfo(&min_version_info
, type_mask
, condition_mask
) != 0;
104 // Returns the current (or installed) browser's ProgId (e.g.
105 // "ChromeHTML|suffix|").
106 // |suffix| can be the empty string.
107 base::string16
GetBrowserProgId(const base::string16
& suffix
) {
108 BrowserDistribution
* dist
= BrowserDistribution::GetDistribution();
109 base::string16
chrome_html(dist
->GetBrowserProgIdPrefix());
110 chrome_html
.append(suffix
);
112 // ProgIds cannot be longer than 39 characters.
113 // Ref: http://msdn.microsoft.com/en-us/library/aa911706.aspx.
114 // Make all new registrations comply with this requirement (existing
115 // registrations must be preserved).
116 base::string16 new_style_suffix
;
117 if (ShellUtil::GetUserSpecificRegistrySuffix(&new_style_suffix
) &&
118 suffix
== new_style_suffix
&& chrome_html
.length() > 39) {
120 chrome_html
.erase(39);
125 // This class is used to initialize and cache a base 32 encoding of the md5 hash
126 // of this user's sid preceded by a dot.
127 // This is guaranteed to be unique on the machine and 27 characters long
128 // (including the '.').
129 // This is then meant to be used as a suffix on all registrations that may
130 // conflict with another user-level Chrome install.
131 class UserSpecificRegistrySuffix
{
133 // All the initialization is done in the constructor to be able to build the
134 // suffix in a thread-safe manner when used in conjunction with a
136 UserSpecificRegistrySuffix();
138 // Sets |suffix| to the pre-computed suffix cached in this object.
139 // Returns true unless the initialization originally failed.
140 bool GetSuffix(base::string16
* suffix
);
143 base::string16 suffix_
;
145 DISALLOW_COPY_AND_ASSIGN(UserSpecificRegistrySuffix
);
146 }; // class UserSpecificRegistrySuffix
148 UserSpecificRegistrySuffix::UserSpecificRegistrySuffix() {
149 base::string16 user_sid
;
150 if (!base::win::GetUserSidString(&user_sid
)) {
154 COMPILE_ASSERT(sizeof(base::MD5Digest
) == 16, size_of_MD5_not_as_expected_
);
155 base::MD5Digest md5_digest
;
156 std::string
user_sid_ascii(base::UTF16ToASCII(user_sid
));
157 base::MD5Sum(user_sid_ascii
.c_str(), user_sid_ascii
.length(), &md5_digest
);
158 const base::string16
base32_md5(
159 ShellUtil::ByteArrayToBase32(md5_digest
.a
, arraysize(md5_digest
.a
)));
160 // The value returned by the base32 algorithm above must never change and
161 // must always be 26 characters long (i.e. if someone ever moves this to
162 // base and implements the full base32 algorithm (i.e. with appended '='
163 // signs in the output), they must provide a flag to allow this method to
164 // still request the output with no appended '=' signs).
165 DCHECK_EQ(base32_md5
.length(), 26U);
166 suffix_
.reserve(base32_md5
.length() + 1);
167 suffix_
.assign(1, L
'.');
168 suffix_
.append(base32_md5
);
171 bool UserSpecificRegistrySuffix::GetSuffix(base::string16
* suffix
) {
172 if (suffix_
.empty()) {
176 suffix
->assign(suffix_
);
180 // This class represents a single registry entry. The objective is to
181 // encapsulate all the registry entries required for registering Chrome at one
182 // place. This class can not be instantiated outside the class and the objects
183 // of this class type can be obtained only by calling a static method of this
185 class RegistryEntry
{
187 // A bit-field enum of places to look for this key in the Windows registry.
189 LOOK_IN_HKCU
= 1 << 0,
190 LOOK_IN_HKLM
= 1 << 1,
191 LOOK_IN_HKCU_THEN_HKLM
= LOOK_IN_HKCU
| LOOK_IN_HKLM
,
194 // Details about a Windows application, to be entered into the registry for
195 // the purpose of file associations.
196 struct ApplicationInfo
{
197 ApplicationInfo() : file_type_icon_index(0), application_icon_index(0) {}
199 // The ProgId used by Windows for file associations with this application.
200 // Must not be empty or start with a '.'.
201 base::string16 prog_id
;
202 // The friendly name, and the path of the icon that will be used for files
203 // of these types when associated with this application by default. (They
204 // are NOT the name/icon that will represent the application under the Open
206 base::string16 file_type_name
;
207 base::FilePath file_type_icon_path
;
208 int file_type_icon_index
;
209 // The command to execute when opening a file via this association. It
210 // should contain "%1" (to tell Windows to pass the filename as an
212 // TODO(mgiuca): |command_line| should be a base::CommandLine.
213 base::string16 command_line
;
214 // The AppUserModelId used by Windows 8 for this application. Distinct from
216 base::string16 app_id
;
218 // User-visible details about this application. Any of these may be empty.
219 base::string16 application_name
;
220 base::FilePath application_icon_path
;
221 int application_icon_index
;
222 base::string16 application_description
;
223 base::string16 publisher_name
;
225 // The CLSID for the application's DelegateExecute handler. May be empty.
226 base::string16 delegate_clsid
;
229 // Returns the Windows browser client registration key for Chrome. For
230 // example: "Software\Clients\StartMenuInternet\Chromium[.user]". Strictly
231 // speaking, we should use the name of the executable (e.g., "chrome.exe"),
232 // but that ship has sailed. The cost of switching now is re-prompting users
233 // to make Chrome their default browser, which isn't polite. |suffix| is the
234 // user-specific registration suffix; see GetUserSpecificDefaultBrowserSuffix
235 // in shell_util.h for details.
236 static base::string16
GetBrowserClientKey(BrowserDistribution
* dist
,
237 const base::string16
& suffix
) {
238 DCHECK(suffix
.empty() || suffix
[0] == L
'.');
239 return base::string16(ShellUtil::kRegStartMenuInternet
)
241 .append(dist
->GetBaseAppName())
245 // Returns the Windows Default Programs capabilities key for Chrome. For
247 // "Software\Clients\StartMenuInternet\Chromium[.user]\Capabilities".
248 static base::string16
GetCapabilitiesKey(BrowserDistribution
* dist
,
249 const base::string16
& suffix
) {
250 return GetBrowserClientKey(dist
, suffix
).append(L
"\\Capabilities");
253 // This method returns a list of all the registry entries that
254 // are needed to register this installation's ProgId and AppId.
255 // These entries need to be registered in HKLM prior to Win8.
256 static void GetChromeProgIdEntries(BrowserDistribution
* dist
,
257 const base::FilePath
& chrome_exe
,
258 const base::string16
& suffix
,
259 ScopedVector
<RegistryEntry
>* entries
) {
260 int chrome_icon_index
=
261 dist
->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME
);
263 ApplicationInfo app_info
;
264 app_info
.prog_id
= GetBrowserProgId(suffix
);
265 app_info
.file_type_name
= dist
->GetBrowserProgIdDesc();
266 // File types associated with Chrome are just given the Chrome icon.
267 app_info
.file_type_icon_path
= chrome_exe
;
268 app_info
.file_type_icon_index
= chrome_icon_index
;
269 app_info
.command_line
= ShellUtil::GetChromeShellOpenCmd(chrome_exe
);
270 // For user-level installs: entries for the app id will be in HKCU; thus we
271 // do not need a suffix on those entries.
272 app_info
.app_id
= ShellUtil::GetBrowserModelId(
273 dist
, InstallUtil::IsPerUserInstall(chrome_exe
));
275 // The command to execute when opening this application via the Metro UI.
276 base::string16
delegate_command(
277 ShellUtil::GetChromeDelegateCommand(chrome_exe
));
278 bool set_delegate_execute
=
279 IsChromeMetroSupported() &&
280 dist
->GetCommandExecuteImplClsid(&app_info
.delegate_clsid
);
282 // TODO(grt): http://crbug.com/75152 Write a reference to a localized
283 // resource for name, description, and company.
284 app_info
.application_name
= dist
->GetDisplayName();
285 app_info
.application_icon_path
= chrome_exe
;
286 app_info
.application_icon_index
= chrome_icon_index
;
287 app_info
.application_description
= dist
->GetAppDescription();
288 app_info
.publisher_name
= dist
->GetPublisherName();
290 GetProgIdEntries(app_info
, entries
);
292 // DelegateExecute ProgId. Needed for Chrome Metro in Windows 8. This is
293 // only needed for registring a web browser, not for general associations.
294 if (set_delegate_execute
) {
295 base::string16
model_id_shell(ShellUtil::kRegClasses
);
296 model_id_shell
.push_back(base::FilePath::kSeparators
[0]);
297 model_id_shell
.append(app_info
.app_id
);
298 model_id_shell
.append(ShellUtil::kRegExePath
);
299 model_id_shell
.append(ShellUtil::kRegShellPath
);
301 // <root hkey>\Software\Classes\<app_id>\.exe\shell @=open
302 entries
->push_back(new RegistryEntry(model_id_shell
,
303 ShellUtil::kRegVerbOpen
));
305 // Each of Chrome's shortcuts has an appid; which, as of Windows 8, is
306 // registered to handle some verbs. This registration has the side-effect
307 // that these verbs now show up in the shortcut's context menu. We
308 // mitigate this side-effect by making the context menu entries
309 // user readable/localized strings. See relevant MSDN article:
310 // http://msdn.microsoft.com/en-US/library/windows/desktop/cc144171.aspx
315 { ShellUtil::kRegVerbOpen
, -1 },
316 { ShellUtil::kRegVerbOpenNewWindow
, IDS_SHORTCUT_NEW_WINDOW_BASE
},
318 for (size_t i
= 0; i
< arraysize(verbs
); ++i
) {
319 base::string16
sub_path(model_id_shell
);
320 sub_path
.push_back(base::FilePath::kSeparators
[0]);
321 sub_path
.append(verbs
[i
].verb
);
323 // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb>
324 if (verbs
[i
].name_id
!= -1) {
325 // TODO(grt): http://crbug.com/75152 Write a reference to a localized
327 base::string16
verb_name(
328 installer::GetLocalizedString(verbs
[i
].name_id
));
329 entries
->push_back(new RegistryEntry(sub_path
, verb_name
.c_str()));
331 entries
->push_back(new RegistryEntry(
332 sub_path
, L
"CommandId", L
"Browser.Launch"));
334 sub_path
.push_back(base::FilePath::kSeparators
[0]);
335 sub_path
.append(ShellUtil::kRegCommand
);
337 // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb>\command
338 entries
->push_back(new RegistryEntry(sub_path
, delegate_command
));
339 entries
->push_back(new RegistryEntry(
340 sub_path
, ShellUtil::kRegDelegateExecute
, app_info
.delegate_clsid
));
345 // Gets the registry entries to register an application in the Windows
346 // registry. |app_info| provides all of the information needed.
347 static void GetProgIdEntries(const ApplicationInfo
& app_info
,
348 ScopedVector
<RegistryEntry
>* entries
) {
349 // Basic sanity checks.
350 DCHECK(!app_info
.prog_id
.empty());
351 DCHECK_NE(L
'.', app_info
.prog_id
[0]);
353 // File association ProgId
354 base::string16
prog_id_path(ShellUtil::kRegClasses
);
355 prog_id_path
.push_back(base::FilePath::kSeparators
[0]);
356 prog_id_path
.append(app_info
.prog_id
);
358 new RegistryEntry(prog_id_path
, app_info
.file_type_name
));
359 entries
->push_back(new RegistryEntry(
360 prog_id_path
+ ShellUtil::kRegDefaultIcon
,
361 ShellUtil::FormatIconLocation(app_info
.file_type_icon_path
,
362 app_info
.file_type_icon_index
)));
363 entries
->push_back(new RegistryEntry(
364 prog_id_path
+ ShellUtil::kRegShellOpen
, app_info
.command_line
));
365 if (!app_info
.delegate_clsid
.empty()) {
367 new RegistryEntry(prog_id_path
+ ShellUtil::kRegShellOpen
,
368 ShellUtil::kRegDelegateExecute
,
369 app_info
.delegate_clsid
));
372 // The following entries are required as of Windows 8, but do not
373 // depend on the DelegateExecute verb handler being set.
374 if (base::win::GetVersion() >= base::win::VERSION_WIN8
) {
375 if (!app_info
.app_id
.empty()) {
376 entries
->push_back(new RegistryEntry(
377 prog_id_path
, ShellUtil::kRegAppUserModelId
, app_info
.app_id
));
380 // Add \Software\Classes\<prog_id>\Application entries
381 base::string16
application_path(prog_id_path
+
382 ShellUtil::kRegApplication
);
383 if (!app_info
.app_id
.empty()) {
384 entries
->push_back(new RegistryEntry(
385 application_path
, ShellUtil::kRegAppUserModelId
, app_info
.app_id
));
387 if (!app_info
.application_icon_path
.empty()) {
388 entries
->push_back(new RegistryEntry(
390 ShellUtil::kRegApplicationIcon
,
391 ShellUtil::FormatIconLocation(app_info
.application_icon_path
,
392 app_info
.application_icon_index
)));
394 if (!app_info
.application_name
.empty()) {
395 entries
->push_back(new RegistryEntry(application_path
,
396 ShellUtil::kRegApplicationName
,
397 app_info
.application_name
));
399 if (!app_info
.application_description
.empty()) {
401 new RegistryEntry(application_path
,
402 ShellUtil::kRegApplicationDescription
,
403 app_info
.application_description
));
405 if (!app_info
.publisher_name
.empty()) {
406 entries
->push_back(new RegistryEntry(application_path
,
407 ShellUtil::kRegApplicationCompany
,
408 app_info
.publisher_name
));
413 // This method returns a list of the registry entries needed to declare a
414 // capability of handling a protocol on Windows.
415 static void GetProtocolCapabilityEntries(
416 BrowserDistribution
* dist
,
417 const base::string16
& suffix
,
418 const base::string16
& protocol
,
419 ScopedVector
<RegistryEntry
>* entries
) {
420 entries
->push_back(new RegistryEntry(
421 GetCapabilitiesKey(dist
, suffix
).append(L
"\\URLAssociations"),
422 protocol
, GetBrowserProgId(suffix
)));
425 // This method returns a list of the registry entries required to register
426 // this installation in "RegisteredApplications" on Windows (to appear in
427 // Default Programs, StartMenuInternet, etc.).
428 // These entries need to be registered in HKLM prior to Win8.
429 // If |suffix| is not empty, these entries are guaranteed to be unique on this
431 static void GetShellIntegrationEntries(BrowserDistribution
* dist
,
432 const base::FilePath
& chrome_exe
,
433 const base::string16
& suffix
,
434 ScopedVector
<RegistryEntry
>* entries
) {
435 const base::string16
icon_path(
436 ShellUtil::FormatIconLocation(
438 dist
->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME
)));
439 const base::string16
quoted_exe_path(L
"\"" + chrome_exe
.value() + L
"\"");
441 // Register for the Start Menu "Internet" link (pre-Win7).
442 const base::string16
start_menu_entry(GetBrowserClientKey(dist
, suffix
));
443 // Register Chrome's display name.
444 // TODO(grt): http://crbug.com/75152 Also set LocalizedString; see
445 // http://msdn.microsoft.com/en-us/library/windows/desktop/cc144109(v=VS.85).aspx#registering_the_display_name
446 entries
->push_back(new RegistryEntry(
447 start_menu_entry
, dist
->GetDisplayName()));
448 // Register the "open" verb for launching Chrome via the "Internet" link.
449 entries
->push_back(new RegistryEntry(
450 start_menu_entry
+ ShellUtil::kRegShellOpen
, quoted_exe_path
));
451 // Register Chrome's icon for the Start Menu "Internet" link.
452 entries
->push_back(new RegistryEntry(
453 start_menu_entry
+ ShellUtil::kRegDefaultIcon
, icon_path
));
455 // Register installation information.
456 base::string16
install_info(start_menu_entry
+ L
"\\InstallInfo");
457 // Note: not using CommandLine since it has ambiguous rules for quoting
459 entries
->push_back(new RegistryEntry(install_info
, kReinstallCommand
,
460 quoted_exe_path
+ L
" --" +
461 base::ASCIIToUTF16(switches::kMakeDefaultBrowser
)));
462 entries
->push_back(new RegistryEntry(install_info
, L
"HideIconsCommand",
463 quoted_exe_path
+ L
" --" +
464 base::ASCIIToUTF16(switches::kHideIcons
)));
465 entries
->push_back(new RegistryEntry(install_info
, L
"ShowIconsCommand",
466 quoted_exe_path
+ L
" --" +
467 base::ASCIIToUTF16(switches::kShowIcons
)));
468 entries
->push_back(new RegistryEntry(install_info
, L
"IconsVisible", 1));
470 // Register with Default Programs.
471 const base::string16
reg_app_name(dist
->GetBaseAppName().append(suffix
));
472 // Tell Windows where to find Chrome's Default Programs info.
473 const base::string16
capabilities(GetCapabilitiesKey(dist
, suffix
));
474 entries
->push_back(new RegistryEntry(ShellUtil::kRegRegisteredApplications
,
475 reg_app_name
, capabilities
));
476 // Write out Chrome's Default Programs info.
477 // TODO(grt): http://crbug.com/75152 Write a reference to a localized
478 // resource rather than this.
479 entries
->push_back(new RegistryEntry(
480 capabilities
, ShellUtil::kRegApplicationDescription
,
481 dist
->GetLongAppDescription()));
482 entries
->push_back(new RegistryEntry(
483 capabilities
, ShellUtil::kRegApplicationIcon
, icon_path
));
484 entries
->push_back(new RegistryEntry(
485 capabilities
, ShellUtil::kRegApplicationName
,
486 dist
->GetDisplayName()));
488 entries
->push_back(new RegistryEntry(capabilities
+ L
"\\Startmenu",
489 L
"StartMenuInternet", reg_app_name
));
491 const base::string16
html_prog_id(GetBrowserProgId(suffix
));
492 for (int i
= 0; ShellUtil::kPotentialFileAssociations
[i
] != NULL
; i
++) {
493 entries
->push_back(new RegistryEntry(
494 capabilities
+ L
"\\FileAssociations",
495 ShellUtil::kPotentialFileAssociations
[i
], html_prog_id
));
497 for (int i
= 0; ShellUtil::kPotentialProtocolAssociations
[i
] != NULL
;
499 entries
->push_back(new RegistryEntry(
500 capabilities
+ L
"\\URLAssociations",
501 ShellUtil::kPotentialProtocolAssociations
[i
], html_prog_id
));
505 // This method returns a list of the registry entries required for this
506 // installation to be registered in the Windows shell.
509 // http://msdn.microsoft.com/en-us/library/windows/desktop/ee872121
510 // - File Associations
511 // http://msdn.microsoft.com/en-us/library/bb166549
512 // These entries need to be registered in HKLM prior to Win8.
513 static void GetChromeAppRegistrationEntries(
514 const base::FilePath
& chrome_exe
,
515 const base::string16
& suffix
,
516 ScopedVector
<RegistryEntry
>* entries
) {
517 base::string16
app_path_key(ShellUtil::kAppPathsRegistryKey
);
518 app_path_key
.push_back(base::FilePath::kSeparators
[0]);
519 app_path_key
.append(chrome_exe
.BaseName().value());
520 entries
->push_back(new RegistryEntry(app_path_key
, chrome_exe
.value()));
521 entries
->push_back(new RegistryEntry(app_path_key
,
522 ShellUtil::kAppPathsRegistryPathName
,
523 chrome_exe
.DirName().value()));
525 const base::string16
html_prog_id(GetBrowserProgId(suffix
));
526 for (int i
= 0; ShellUtil::kPotentialFileAssociations
[i
] != NULL
; i
++) {
527 GetAppExtRegistrationEntries(
528 html_prog_id
, ShellUtil::kPotentialFileAssociations
[i
], entries
);
532 // Gets the registry entries to register an application as a handler for a
533 // particular file extension. |prog_id| is the ProgId used by Windows for the
534 // application. |ext| is the file extension, which must begin with a '.'.
535 static void GetAppExtRegistrationEntries(
536 const base::string16
& prog_id
,
537 const base::string16
& ext
,
538 ScopedVector
<RegistryEntry
>* entries
) {
539 // In HKEY_CURRENT_USER\Software\Classes\EXT\OpenWithProgids, create an
540 // empty value with this class's ProgId.
541 base::string16
key_name(ShellUtil::kRegClasses
);
542 key_name
.push_back(base::FilePath::kSeparators
[0]);
543 key_name
.append(ext
);
544 key_name
.push_back(base::FilePath::kSeparators
[0]);
545 key_name
.append(ShellUtil::kRegOpenWithProgids
);
546 entries
->push_back(new RegistryEntry(key_name
, prog_id
, base::string16()));
549 // Gets the registry entries to register an application as the default handler
550 // for a particular file extension. |prog_id| is the ProgId used by Windows
551 // for the application. |ext| is the file extension, which must begin with a
552 // '.'. If |overwrite_existing|, always sets the default handler; otherwise
553 // only sets if there is no existing default.
555 // This has no effect on Windows 8. Windows 8 ignores the default and lets the
556 // user choose. If there is only one handler for a file, it will automatically
557 // become the default. Otherwise, the first time the user opens a file, they
558 // are presented with the dialog to set the default handler. (This is roughly
559 // equivalent to being called with |overwrite_existing| false.)
560 static void GetAppDefaultRegistrationEntries(
561 const base::string16
& prog_id
,
562 const base::string16
& ext
,
563 bool overwrite_existing
,
564 ScopedVector
<RegistryEntry
>* entries
) {
565 // Set the default value of HKEY_CURRENT_USER\Software\Classes\EXT to this
567 base::string16
key_name(ShellUtil::kRegClasses
);
568 key_name
.push_back(base::FilePath::kSeparators
[0]);
569 key_name
.append(ext
);
570 scoped_ptr
<RegistryEntry
> default_association(
571 new RegistryEntry(key_name
, prog_id
));
572 if (overwrite_existing
||
573 !default_association
->KeyExistsInRegistry(
574 RegistryEntry::LOOK_IN_HKCU
)) {
575 entries
->push_back(default_association
.release());
579 // This method returns a list of all the user level registry entries that
580 // are needed to make Chromium the default handler for a protocol on XP.
581 static void GetXPStyleUserProtocolEntries(
582 const base::string16
& protocol
,
583 const base::string16
& chrome_icon
,
584 const base::string16
& chrome_open
,
585 ScopedVector
<RegistryEntry
>* entries
) {
586 // Protocols associations.
587 base::string16
url_key(ShellUtil::kRegClasses
);
588 url_key
.push_back(base::FilePath::kSeparators
[0]);
589 url_key
.append(protocol
);
591 // This registry value tells Windows that this 'class' is a URL scheme
592 // so IE, explorer and other apps will route it to our handler.
593 // <root hkey>\Software\Classes\<protocol>\URL Protocol
594 entries
->push_back(new RegistryEntry(url_key
,
595 ShellUtil::kRegUrlProtocol
, base::string16()));
597 // <root hkey>\Software\Classes\<protocol>\DefaultIcon
598 base::string16 icon_key
= url_key
+ ShellUtil::kRegDefaultIcon
;
599 entries
->push_back(new RegistryEntry(icon_key
, chrome_icon
));
601 // <root hkey>\Software\Classes\<protocol>\shell\open\command
602 base::string16 shell_key
= url_key
+ ShellUtil::kRegShellOpen
;
603 entries
->push_back(new RegistryEntry(shell_key
, chrome_open
));
605 // <root hkey>\Software\Classes\<protocol>\shell\open\ddeexec
606 base::string16 dde_key
= url_key
+ L
"\\shell\\open\\ddeexec";
607 entries
->push_back(new RegistryEntry(dde_key
, base::string16()));
609 // <root hkey>\Software\Classes\<protocol>\shell\@
610 base::string16 protocol_shell_key
= url_key
+ ShellUtil::kRegShellPath
;
611 entries
->push_back(new RegistryEntry(protocol_shell_key
, L
"open"));
614 // This method returns a list of all the user level registry entries that
615 // are needed to make Chromium default browser on XP.
616 // Some of these entries are irrelevant in recent versions of Windows, but
617 // we register them anyways as some legacy apps are hardcoded to lookup those
619 static void GetXPStyleDefaultBrowserUserEntries(
620 BrowserDistribution
* dist
,
621 const base::FilePath
& chrome_exe
,
622 const base::string16
& suffix
,
623 ScopedVector
<RegistryEntry
>* entries
) {
624 // File extension associations.
625 base::string16
html_prog_id(GetBrowserProgId(suffix
));
626 for (int i
= 0; ShellUtil::kDefaultFileAssociations
[i
] != NULL
; i
++) {
627 GetAppDefaultRegistrationEntries(
628 html_prog_id
, ShellUtil::kDefaultFileAssociations
[i
], true, entries
);
631 // Protocols associations.
632 base::string16 chrome_open
= ShellUtil::GetChromeShellOpenCmd(chrome_exe
);
633 base::string16 chrome_icon
=
634 ShellUtil::FormatIconLocation(
636 dist
->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME
));
637 for (int i
= 0; ShellUtil::kBrowserProtocolAssociations
[i
] != NULL
; i
++) {
638 GetXPStyleUserProtocolEntries(ShellUtil::kBrowserProtocolAssociations
[i
],
639 chrome_icon
, chrome_open
, entries
);
642 // start->Internet shortcut.
643 base::string16
start_menu(ShellUtil::kRegStartMenuInternet
);
644 base::string16 app_name
= dist
->GetBaseAppName() + suffix
;
645 entries
->push_back(new RegistryEntry(start_menu
, app_name
));
648 // Generate work_item tasks required to create current registry entry and
649 // add them to the given work item list.
650 void AddToWorkItemList(HKEY root
, WorkItemList
*items
) const {
651 items
->AddCreateRegKeyWorkItem(root
, key_path_
, WorkItem::kWow64Default
);
653 items
->AddSetRegValueWorkItem(
654 root
, key_path_
, WorkItem::kWow64Default
, name_
, value_
, true);
656 items
->AddSetRegValueWorkItem(
657 root
, key_path_
, WorkItem::kWow64Default
, name_
, int_value_
, true);
661 // Checks if the current registry entry exists in HKCU\|key_path_|\|name_|
662 // and value is |value_|. If the key does NOT exist in HKCU, checks for
663 // the correct name and value in HKLM.
664 // |look_for_in| specifies roots (HKCU and/or HKLM) in which to look for the
665 // key, unspecified roots are not looked into (i.e. the the key is assumed not
666 // to exist in them).
667 // |look_for_in| must at least specify one root to look into.
668 // If |look_for_in| is LOOK_IN_HKCU_THEN_HKLM, this method mimics Windows'
669 // behavior when searching in HKCR (HKCU takes precedence over HKLM). For
670 // registrations outside of HKCR on versions of Windows prior to Win8,
671 // Chrome's values go in HKLM. This function will make unnecessary (but
672 // harmless) queries into HKCU in that case.
673 bool ExistsInRegistry(uint32 look_for_in
) const {
676 RegistryStatus status
= DOES_NOT_EXIST
;
677 if (look_for_in
& LOOK_IN_HKCU
)
678 status
= StatusInRegistryUnderRoot(HKEY_CURRENT_USER
);
679 if (status
== DOES_NOT_EXIST
&& (look_for_in
& LOOK_IN_HKLM
))
680 status
= StatusInRegistryUnderRoot(HKEY_LOCAL_MACHINE
);
681 return status
== SAME_VALUE
;
684 // Checks if the current registry entry exists in \|key_path_|\|name_|,
685 // regardless of value. Same lookup rules as ExistsInRegistry.
686 // Unlike ExistsInRegistry, this returns true if some other value is present
687 // with the same key.
688 bool KeyExistsInRegistry(uint32 look_for_in
) const {
691 RegistryStatus status
= DOES_NOT_EXIST
;
692 if (look_for_in
& LOOK_IN_HKCU
)
693 status
= StatusInRegistryUnderRoot(HKEY_CURRENT_USER
);
694 if (status
== DOES_NOT_EXIST
&& (look_for_in
& LOOK_IN_HKLM
))
695 status
= StatusInRegistryUnderRoot(HKEY_LOCAL_MACHINE
);
696 return status
!= DOES_NOT_EXIST
;
700 // States this RegistryKey can be in compared to the registry.
701 enum RegistryStatus
{
702 // |name_| does not exist in the registry
704 // |name_| exists, but its value != |value_|
706 // |name_| exists and its value is |value_|
710 // Create a object that represent default value of a key
711 RegistryEntry(const base::string16
& key_path
, const base::string16
& value
)
712 : key_path_(key_path
), name_(),
713 is_string_(true), value_(value
), int_value_(0) {
716 // Create a object that represent a key of type REG_SZ
717 RegistryEntry(const base::string16
& key_path
, const base::string16
& name
,
718 const base::string16
& value
)
719 : key_path_(key_path
), name_(name
),
720 is_string_(true), value_(value
), int_value_(0) {
723 // Create a object that represent a key of integer type
724 RegistryEntry(const base::string16
& key_path
, const base::string16
& name
,
726 : key_path_(key_path
), name_(name
),
727 is_string_(false), value_(), int_value_(value
) {
730 base::string16 key_path_
; // key path for the registry entry
731 base::string16 name_
; // name of the registry entry
732 bool is_string_
; // true if current registry entry is of type REG_SZ
733 base::string16 value_
; // string value (useful if is_string_ = true)
734 DWORD int_value_
; // integer value (useful if is_string_ = false)
736 // Helper function for ExistsInRegistry().
737 // Returns the RegistryStatus of the current registry entry in
738 // |root|\|key_path_|\|name_|.
739 RegistryStatus
StatusInRegistryUnderRoot(HKEY root
) const {
740 RegKey
key(root
, key_path_
.c_str(), KEY_QUERY_VALUE
);
742 bool correct_value
= false;
744 base::string16 read_value
;
745 found
= key
.ReadValue(name_
.c_str(), &read_value
) == ERROR_SUCCESS
;
747 correct_value
= read_value
.size() == value_
.size() &&
748 std::equal(value_
.begin(), value_
.end(), read_value
.begin(),
749 base::CaseInsensitiveCompare
<wchar_t>());
753 found
= key
.ReadValueDW(name_
.c_str(), &read_value
) == ERROR_SUCCESS
;
755 correct_value
= read_value
== int_value_
;
758 (correct_value
? SAME_VALUE
: DIFFERENT_VALUE
) : DOES_NOT_EXIST
;
761 DISALLOW_COPY_AND_ASSIGN(RegistryEntry
);
762 }; // class RegistryEntry
765 // This method converts all the RegistryEntries from the given list to
766 // Set/CreateRegWorkItems and runs them using WorkItemList.
767 bool AddRegistryEntries(HKEY root
, const ScopedVector
<RegistryEntry
>& entries
) {
768 scoped_ptr
<WorkItemList
> items(WorkItem::CreateWorkItemList());
770 for (ScopedVector
<RegistryEntry
>::const_iterator itr
= entries
.begin();
771 itr
!= entries
.end(); ++itr
)
772 (*itr
)->AddToWorkItemList(root
, items
.get());
774 // Apply all the registry changes and if there is a problem, rollback
782 // Checks that all |entries| are present on this computer.
783 // |look_for_in| is passed to RegistryEntry::ExistsInRegistry(). Documentation
784 // for it can be found there.
785 bool AreEntriesRegistered(const ScopedVector
<RegistryEntry
>& entries
,
786 uint32 look_for_in
) {
787 bool registered
= true;
788 for (ScopedVector
<RegistryEntry
>::const_iterator itr
= entries
.begin();
789 registered
&& itr
!= entries
.end(); ++itr
) {
790 // We do not need registered = registered && ... since the loop condition
791 // is set to exit early.
792 registered
= (*itr
)->ExistsInRegistry(look_for_in
);
797 // Checks that all required registry entries for Chrome are already present
798 // on this computer. See RegistryEntry::ExistsInRegistry for the behavior of
800 // Note: between r133333 and r154145 we were registering parts of Chrome in HKCU
801 // and parts in HKLM for user-level installs; we now always register everything
802 // under a single registry root. Not doing so caused http://crbug.com/144910 for
803 // users who first-installed Chrome in that revision range (those users are
804 // still impacted by http://crbug.com/144910). This method will keep returning
805 // true for affected users (i.e. who have all the registrations, but over both
807 bool IsChromeRegistered(BrowserDistribution
* dist
,
808 const base::FilePath
& chrome_exe
,
809 const base::string16
& suffix
,
810 uint32 look_for_in
) {
811 ScopedVector
<RegistryEntry
> entries
;
812 RegistryEntry::GetChromeProgIdEntries(dist
, chrome_exe
, suffix
, &entries
);
813 RegistryEntry::GetShellIntegrationEntries(dist
, chrome_exe
, suffix
, &entries
);
814 RegistryEntry::GetChromeAppRegistrationEntries(chrome_exe
, suffix
, &entries
);
815 return AreEntriesRegistered(entries
, look_for_in
);
818 // This method checks if Chrome is already registered on the local machine
819 // for the requested protocol. It just checks the one value required for this.
820 // See RegistryEntry::ExistsInRegistry for the behavior of |look_for_in|.
821 bool IsChromeRegisteredForProtocol(BrowserDistribution
* dist
,
822 const base::string16
& suffix
,
823 const base::string16
& protocol
,
824 uint32 look_for_in
) {
825 ScopedVector
<RegistryEntry
> entries
;
826 RegistryEntry::GetProtocolCapabilityEntries(dist
, suffix
, protocol
, &entries
);
827 return AreEntriesRegistered(entries
, look_for_in
);
830 // This method registers Chrome on Vista by launching an elevated setup.exe.
831 // That will show the user the standard Vista elevation prompt. If the user
832 // accepts it the new process will make the necessary changes and return SUCCESS
833 // that we capture and return.
834 // If protocol is non-empty we will also register Chrome as being capable of
835 // handling the protocol.
836 bool ElevateAndRegisterChrome(BrowserDistribution
* dist
,
837 const base::FilePath
& chrome_exe
,
838 const base::string16
& suffix
,
839 const base::string16
& protocol
) {
840 // Only user-level installs prior to Windows 8 should need to elevate to
842 DCHECK(InstallUtil::IsPerUserInstall(chrome_exe
));
843 DCHECK_LT(base::win::GetVersion(), base::win::VERSION_WIN8
);
844 base::FilePath exe_path
= chrome_exe
.DirName().Append(installer::kSetupExe
);
845 if (!base::PathExists(exe_path
)) {
846 HKEY reg_root
= InstallUtil::IsPerUserInstall(chrome_exe
) ?
847 HKEY_CURRENT_USER
: HKEY_LOCAL_MACHINE
;
849 dist
->GetUninstallRegPath().c_str(),
850 KEY_READ
| KEY_WOW64_32KEY
);
851 base::string16 uninstall_string
;
852 key
.ReadValue(installer::kUninstallStringField
, &uninstall_string
);
853 base::CommandLine command_line
=
854 base::CommandLine::FromString(uninstall_string
);
855 exe_path
= command_line
.GetProgram();
858 if (base::PathExists(exe_path
)) {
859 base::CommandLine
cmd(exe_path
);
860 cmd
.AppendSwitchPath(installer::switches::kRegisterChromeBrowser
,
862 if (!suffix
.empty()) {
863 cmd
.AppendSwitchNative(
864 installer::switches::kRegisterChromeBrowserSuffix
, suffix
);
867 if (!protocol
.empty()) {
868 cmd
.AppendSwitchNative(
869 installer::switches::kRegisterURLProtocol
, protocol
);
873 InstallUtil::ExecuteExeAsAdmin(cmd
, &ret_val
);
880 // Launches the Windows 7 and Windows 8 dialog for picking the application to
881 // handle the given protocol. Most importantly, this is used to set the default
882 // handler for http (and, implicitly with it, https). In that case it is also
883 // known as the 'how do you want to open webpages' dialog.
884 // It is required that Chrome be already *registered* for the given protocol.
885 bool LaunchSelectDefaultProtocolHandlerDialog(const wchar_t* protocol
) {
887 OPENASINFO open_as_info
= {};
888 open_as_info
.pcszFile
= protocol
;
889 open_as_info
.oaifInFlags
=
890 OAIF_URL_PROTOCOL
| OAIF_FORCE_REGISTRATION
| OAIF_REGISTER_EXT
;
891 HRESULT hr
= SHOpenWithDialog(NULL
, &open_as_info
);
892 DLOG_IF(WARNING
, FAILED(hr
)) << "Failed to set as default " << protocol
893 << " handler; hr=0x" << std::hex
<< hr
;
896 SHChangeNotify(SHCNE_ASSOCCHANGED
, SHCNF_IDLIST
, NULL
, NULL
);
900 // Launches the Windows 7 and Windows 8 application association dialog, which
901 // is the only documented way to make a browser the default browser on
903 bool LaunchApplicationAssociationDialog(const base::string16
& app_id
) {
904 base::win::ScopedComPtr
<IApplicationAssociationRegistrationUI
> aarui
;
905 HRESULT hr
= aarui
.CreateInstance(CLSID_ApplicationAssociationRegistrationUI
);
908 hr
= aarui
->LaunchAdvancedAssociationUI(app_id
.c_str());
909 return SUCCEEDED(hr
);
912 // Returns true if the current install's |chrome_exe| has been registered with
914 // |confirmation_level| is the level of verification desired as described in
915 // the RegistrationConfirmationLevel enum above.
916 // |suffix| can be the empty string (this is used to support old installs
917 // where we used to not suffix user-level installs if they were the first to
918 // request the non-suffixed registry entries on the machine).
919 // NOTE: This a quick check that only validates that a single registry entry
920 // points to |chrome_exe|. This should only be used at run-time to determine
921 // how Chrome is registered, not to know whether the registration is complete
922 // at install-time (IsChromeRegistered() can be used for that).
923 bool QuickIsChromeRegistered(BrowserDistribution
* dist
,
924 const base::FilePath
& chrome_exe
,
925 const base::string16
& suffix
,
926 RegistrationConfirmationLevel confirmation_level
) {
927 // Get the appropriate key to look for based on the level desired.
928 base::string16 reg_key
;
929 switch (confirmation_level
) {
930 case CONFIRM_PROGID_REGISTRATION
:
931 // Software\Classes\ChromeHTML|suffix|
932 reg_key
= ShellUtil::kRegClasses
;
933 reg_key
.push_back(base::FilePath::kSeparators
[0]);
934 reg_key
.append(dist
->GetBrowserProgIdPrefix());
935 reg_key
.append(suffix
);
937 case CONFIRM_SHELL_REGISTRATION
:
938 case CONFIRM_SHELL_REGISTRATION_IN_HKLM
:
939 // Software\Clients\StartMenuInternet\Google Chrome|suffix|
940 reg_key
= RegistryEntry::GetBrowserClientKey(dist
, suffix
);
946 reg_key
.append(ShellUtil::kRegShellOpen
);
948 // ProgId registrations are allowed to reside in HKCU for user-level installs
949 // (and values there have priority over values in HKLM). The same is true for
950 // shell integration entries as of Windows 8.
951 if (confirmation_level
== CONFIRM_PROGID_REGISTRATION
||
952 (confirmation_level
== CONFIRM_SHELL_REGISTRATION
&&
953 base::win::GetVersion() >= base::win::VERSION_WIN8
)) {
954 const RegKey
key_hkcu(HKEY_CURRENT_USER
, reg_key
.c_str(), KEY_QUERY_VALUE
);
955 base::string16 hkcu_value
;
956 // If |reg_key| is present in HKCU, assert that it points to |chrome_exe|.
957 // Otherwise, fall back on an HKLM lookup below.
958 if (key_hkcu
.ReadValue(L
"", &hkcu_value
) == ERROR_SUCCESS
)
959 return InstallUtil::ProgramCompare(chrome_exe
).Evaluate(hkcu_value
);
962 // Assert that |reg_key| points to |chrome_exe| in HKLM.
963 const RegKey
key_hklm(HKEY_LOCAL_MACHINE
, reg_key
.c_str(), KEY_QUERY_VALUE
);
964 base::string16 hklm_value
;
965 if (key_hklm
.ReadValue(L
"", &hklm_value
) == ERROR_SUCCESS
)
966 return InstallUtil::ProgramCompare(chrome_exe
).Evaluate(hklm_value
);
970 // Sets |suffix| to a 27 character string that is specific to this user on this
971 // machine (on user-level installs only).
972 // To support old-style user-level installs however, |suffix| is cleared if the
973 // user currently owns the non-suffixed HKLM registrations.
974 // |suffix| can also be set to the user's username if the current install is
975 // suffixed as per the old-style registrations.
976 // |suffix| is cleared on system-level installs.
977 // |suffix| should then be appended to all Chrome properties that may conflict
978 // with other Chrome user-level installs.
979 // Returns true unless one of the underlying calls fails.
980 bool GetInstallationSpecificSuffix(BrowserDistribution
* dist
,
981 const base::FilePath
& chrome_exe
,
982 base::string16
* suffix
) {
983 if (!InstallUtil::IsPerUserInstall(chrome_exe
) ||
984 QuickIsChromeRegistered(dist
, chrome_exe
, base::string16(),
985 CONFIRM_SHELL_REGISTRATION
)) {
986 // No suffix on system-level installs and user-level installs already
987 // registered with no suffix.
992 // Get the old suffix for the check below.
993 if (!ShellUtil::GetOldUserSpecificRegistrySuffix(suffix
)) {
997 if (QuickIsChromeRegistered(dist
, chrome_exe
, *suffix
,
998 CONFIRM_SHELL_REGISTRATION
)) {
999 // Username suffix for installs that are suffixed as per the old-style.
1003 return ShellUtil::GetUserSpecificRegistrySuffix(suffix
);
1006 // Returns the root registry key (HKLM or HKCU) under which registrations must
1007 // be placed for this install. As of Windows 8 everything can go in HKCU for
1008 // per-user installs.
1009 HKEY
DetermineRegistrationRoot(bool is_per_user
) {
1010 return is_per_user
&& base::win::GetVersion() >= base::win::VERSION_WIN8
?
1011 HKEY_CURRENT_USER
: HKEY_LOCAL_MACHINE
;
1014 // Associates Chrome with supported protocols and file associations. This should
1015 // not be required on Vista+ but since some applications still read
1016 // Software\Classes\http key directly, we have to do this on Vista+ as well.
1017 bool RegisterChromeAsDefaultXPStyle(BrowserDistribution
* dist
,
1019 const base::FilePath
& chrome_exe
) {
1021 ScopedVector
<RegistryEntry
> entries
;
1022 RegistryEntry::GetXPStyleDefaultBrowserUserEntries(
1024 ShellUtil::GetCurrentInstallationSuffix(dist
, chrome_exe
), &entries
);
1026 // Change the default browser for current user.
1027 if ((shell_change
& ShellUtil::CURRENT_USER
) &&
1028 !AddRegistryEntries(HKEY_CURRENT_USER
, entries
)) {
1030 LOG(ERROR
) << "Could not make Chrome default browser (XP/current user).";
1033 // Chrome as default browser at system level.
1034 if ((shell_change
& ShellUtil::SYSTEM_LEVEL
) &&
1035 !AddRegistryEntries(HKEY_LOCAL_MACHINE
, entries
)) {
1037 LOG(ERROR
) << "Could not make Chrome default browser (XP/system level).";
1043 // Associates Chrome with |protocol| in the registry. This should not be
1044 // required on Vista+ but since some applications still read these registry
1045 // keys directly, we have to do this on Vista+ as well.
1046 // See http://msdn.microsoft.com/library/aa767914.aspx for more details.
1047 bool RegisterChromeAsDefaultProtocolClientXPStyle(
1048 BrowserDistribution
* dist
,
1049 const base::FilePath
& chrome_exe
,
1050 const base::string16
& protocol
) {
1051 ScopedVector
<RegistryEntry
> entries
;
1052 const base::string16
chrome_open(
1053 ShellUtil::GetChromeShellOpenCmd(chrome_exe
));
1054 const base::string16
chrome_icon(
1055 ShellUtil::FormatIconLocation(
1057 dist
->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME
)));
1058 RegistryEntry::GetXPStyleUserProtocolEntries(protocol
, chrome_icon
,
1059 chrome_open
, &entries
);
1060 // Change the default protocol handler for current user.
1061 if (!AddRegistryEntries(HKEY_CURRENT_USER
, entries
)) {
1062 LOG(ERROR
) << "Could not make Chrome default protocol client (XP).";
1069 // Returns |properties.shortcut_name| if the property is set, otherwise it
1070 // returns dist->GetShortcutName(BrowserDistribution::SHORTCUT_CHROME). In any
1071 // case, it makes sure the return value is suffixed with ".lnk".
1072 base::string16
ExtractShortcutNameFromProperties(
1073 BrowserDistribution
* dist
,
1074 const ShellUtil::ShortcutProperties
& properties
) {
1076 base::string16 shortcut_name
;
1077 if (properties
.has_shortcut_name()) {
1078 shortcut_name
= properties
.shortcut_name
;
1081 dist
->GetShortcutName(BrowserDistribution::SHORTCUT_CHROME
);
1084 if (!EndsWith(shortcut_name
, installer::kLnkExt
, false))
1085 shortcut_name
.append(installer::kLnkExt
);
1087 return shortcut_name
;
1090 // Converts ShellUtil::ShortcutOperation to the best-matching value in
1091 // base::win::ShortcutOperation.
1092 base::win::ShortcutOperation
TranslateShortcutOperation(
1093 ShellUtil::ShortcutOperation operation
) {
1094 switch (operation
) {
1095 case ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS
: // Falls through.
1096 case ShellUtil::SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL
:
1097 return base::win::SHORTCUT_CREATE_ALWAYS
;
1099 case ShellUtil::SHELL_SHORTCUT_UPDATE_EXISTING
:
1100 return base::win::SHORTCUT_UPDATE_EXISTING
;
1102 case ShellUtil::SHELL_SHORTCUT_REPLACE_EXISTING
:
1103 return base::win::SHORTCUT_REPLACE_EXISTING
;
1107 return base::win::SHORTCUT_REPLACE_EXISTING
;
1111 // Returns a base::win::ShortcutProperties struct containing the properties
1112 // to set on the shortcut based on the provided ShellUtil::ShortcutProperties.
1113 base::win::ShortcutProperties
TranslateShortcutProperties(
1114 const ShellUtil::ShortcutProperties
& properties
) {
1115 base::win::ShortcutProperties shortcut_properties
;
1117 if (properties
.has_target()) {
1118 shortcut_properties
.set_target(properties
.target
);
1119 DCHECK(!properties
.target
.DirName().empty());
1120 shortcut_properties
.set_working_dir(properties
.target
.DirName());
1123 if (properties
.has_arguments())
1124 shortcut_properties
.set_arguments(properties
.arguments
);
1126 if (properties
.has_description())
1127 shortcut_properties
.set_description(properties
.description
);
1129 if (properties
.has_icon())
1130 shortcut_properties
.set_icon(properties
.icon
, properties
.icon_index
);
1132 if (properties
.has_app_id())
1133 shortcut_properties
.set_app_id(properties
.app_id
);
1135 if (properties
.has_dual_mode())
1136 shortcut_properties
.set_dual_mode(properties
.dual_mode
);
1138 return shortcut_properties
;
1141 // Cleans up an old verb (run) we used to register in
1142 // <root>\Software\Classes\Chrome<.suffix>\.exe\shell\run on Windows 8.
1143 void RemoveRunVerbOnWindows8(BrowserDistribution
* dist
,
1144 const base::FilePath
& chrome_exe
) {
1145 if (IsChromeMetroSupported()) {
1146 bool is_per_user_install
= InstallUtil::IsPerUserInstall(chrome_exe
);
1147 HKEY root_key
= DetermineRegistrationRoot(is_per_user_install
);
1148 // There's no need to rollback, so forgo the usual work item lists and just
1149 // remove the key from the registry.
1150 base::string16
run_verb_key(ShellUtil::kRegClasses
);
1151 run_verb_key
.push_back(base::FilePath::kSeparators
[0]);
1152 run_verb_key
.append(ShellUtil::GetBrowserModelId(
1153 dist
, is_per_user_install
));
1154 run_verb_key
.append(ShellUtil::kRegExePath
);
1155 run_verb_key
.append(ShellUtil::kRegShellPath
);
1156 run_verb_key
.push_back(base::FilePath::kSeparators
[0]);
1157 run_verb_key
.append(ShellUtil::kRegVerbRun
);
1158 InstallUtil::DeleteRegistryKey(root_key
, run_verb_key
,
1159 WorkItem::kWow64Default
);
1163 // Gets the short (8.3) form of |path|, putting the result in |short_path| and
1164 // returning true on success. |short_path| is not modified on failure.
1165 bool ShortNameFromPath(const base::FilePath
& path
, base::string16
* short_path
) {
1167 base::string16
result(MAX_PATH
, L
'\0');
1168 DWORD short_length
= GetShortPathName(path
.value().c_str(), &result
[0],
1170 if (short_length
== 0 || short_length
> result
.size()) {
1171 PLOG(ERROR
) << "Error getting short (8.3) path";
1175 result
.resize(short_length
);
1176 short_path
->swap(result
);
1180 // Probe using IApplicationAssociationRegistration::QueryCurrentDefault
1181 // (Windows 8); see ProbeProtocolHandlers. This mechanism is not suitable for
1182 // use on previous versions of Windows despite the presence of
1183 // QueryCurrentDefault on them since versions of Windows prior to Windows 8
1184 // did not perform validation on the ProgID registered as the current default.
1185 // As a result, stale ProgIDs could be returned, leading to false positives.
1186 ShellUtil::DefaultState
ProbeCurrentDefaultHandlers(
1187 const base::FilePath
& chrome_exe
,
1188 const wchar_t* const* protocols
,
1189 size_t num_protocols
) {
1190 base::win::ScopedComPtr
<IApplicationAssociationRegistration
> registration
;
1191 HRESULT hr
= registration
.CreateInstance(
1192 CLSID_ApplicationAssociationRegistration
, NULL
, CLSCTX_INPROC
);
1194 return ShellUtil::UNKNOWN_DEFAULT
;
1196 BrowserDistribution
* dist
= BrowserDistribution::GetDistribution();
1197 base::string16
prog_id(dist
->GetBrowserProgIdPrefix());
1198 prog_id
+= ShellUtil::GetCurrentInstallationSuffix(dist
, chrome_exe
);
1200 for (size_t i
= 0; i
< num_protocols
; ++i
) {
1201 base::win::ScopedCoMem
<wchar_t> current_app
;
1202 hr
= registration
->QueryCurrentDefault(protocols
[i
], AT_URLPROTOCOL
,
1203 AL_EFFECTIVE
, ¤t_app
);
1204 if (FAILED(hr
) || prog_id
.compare(current_app
) != 0)
1205 return ShellUtil::NOT_DEFAULT
;
1208 return ShellUtil::IS_DEFAULT
;
1211 // Probe using IApplicationAssociationRegistration::QueryAppIsDefault (Vista and
1212 // Windows 7); see ProbeProtocolHandlers.
1213 ShellUtil::DefaultState
ProbeAppIsDefaultHandlers(
1214 const base::FilePath
& chrome_exe
,
1215 const wchar_t* const* protocols
,
1216 size_t num_protocols
) {
1217 base::win::ScopedComPtr
<IApplicationAssociationRegistration
> registration
;
1218 HRESULT hr
= registration
.CreateInstance(
1219 CLSID_ApplicationAssociationRegistration
, NULL
, CLSCTX_INPROC
);
1221 return ShellUtil::UNKNOWN_DEFAULT
;
1223 BrowserDistribution
* dist
= BrowserDistribution::GetDistribution();
1224 base::string16
app_name(ShellUtil::GetApplicationName(dist
, chrome_exe
));
1227 for (size_t i
= 0; i
< num_protocols
; ++i
) {
1229 hr
= registration
->QueryAppIsDefault(protocols
[i
], AT_URLPROTOCOL
,
1230 AL_EFFECTIVE
, app_name
.c_str(), &result
);
1231 if (FAILED(hr
) || result
== FALSE
)
1232 return ShellUtil::NOT_DEFAULT
;
1235 return ShellUtil::IS_DEFAULT
;
1238 // Probe the current commands registered to handle the shell "open" verb for
1239 // |protocols| (Windows XP); see ProbeProtocolHandlers.
1240 ShellUtil::DefaultState
ProbeOpenCommandHandlers(
1241 const base::FilePath
& chrome_exe
,
1242 const wchar_t* const* protocols
,
1243 size_t num_protocols
) {
1244 // Get its short (8.3) form.
1245 base::string16 short_app_path
;
1246 if (!ShortNameFromPath(chrome_exe
, &short_app_path
))
1247 return ShellUtil::UNKNOWN_DEFAULT
;
1249 const HKEY root_key
= HKEY_CLASSES_ROOT
;
1250 base::string16 key_path
;
1251 base::win::RegKey key
;
1252 base::string16 value
;
1253 base::CommandLine
command_line(base::CommandLine::NO_PROGRAM
);
1254 base::string16 short_path
;
1256 for (size_t i
= 0; i
< num_protocols
; ++i
) {
1257 // Get the command line from HKCU\<protocol>\shell\open\command.
1258 key_path
.assign(protocols
[i
]).append(ShellUtil::kRegShellOpen
);
1259 if ((key
.Open(root_key
, key_path
.c_str(),
1260 KEY_QUERY_VALUE
) != ERROR_SUCCESS
) ||
1261 (key
.ReadValue(L
"", &value
) != ERROR_SUCCESS
)) {
1262 return ShellUtil::NOT_DEFAULT
;
1265 // Need to normalize path in case it's been munged.
1266 command_line
= base::CommandLine::FromString(value
);
1267 if (!ShortNameFromPath(command_line
.GetProgram(), &short_path
))
1268 return ShellUtil::UNKNOWN_DEFAULT
;
1270 if (!base::FilePath::CompareEqualIgnoreCase(short_path
, short_app_path
))
1271 return ShellUtil::NOT_DEFAULT
;
1274 return ShellUtil::IS_DEFAULT
;
1277 // A helper function that probes default protocol handler registration (in a
1278 // manner appropriate for the current version of Windows) to determine if
1279 // Chrome is the default handler for |protocols|. Returns IS_DEFAULT
1280 // only if Chrome is the default for all specified protocols.
1281 ShellUtil::DefaultState
ProbeProtocolHandlers(
1282 const base::FilePath
& chrome_exe
,
1283 const wchar_t* const* protocols
,
1284 size_t num_protocols
) {
1286 DCHECK(!num_protocols
|| protocols
);
1287 for (size_t i
= 0; i
< num_protocols
; ++i
)
1288 DCHECK(protocols
[i
] && *protocols
[i
]);
1291 const base::win::Version windows_version
= base::win::GetVersion();
1293 if (windows_version
>= base::win::VERSION_WIN8
)
1294 return ProbeCurrentDefaultHandlers(chrome_exe
, protocols
, num_protocols
);
1295 else if (windows_version
>= base::win::VERSION_VISTA
)
1296 return ProbeAppIsDefaultHandlers(chrome_exe
, protocols
, num_protocols
);
1298 return ProbeOpenCommandHandlers(chrome_exe
, protocols
, num_protocols
);
1301 // (Windows 8+) Finds and stores an app shortcuts folder path in *|path|.
1302 // Returns true on success.
1303 bool GetAppShortcutsFolder(BrowserDistribution
* dist
,
1304 ShellUtil::ShellChange level
,
1305 base::FilePath
*path
) {
1307 DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8
);
1309 base::FilePath folder
;
1310 if (!PathService::Get(base::DIR_APP_SHORTCUTS
, &folder
)) {
1311 LOG(ERROR
) << "Could not get application shortcuts location.";
1315 folder
= folder
.Append(
1316 ShellUtil::GetBrowserModelId(dist
, level
== ShellUtil::CURRENT_USER
));
1317 if (!base::DirectoryExists(folder
)) {
1318 VLOG(1) << "No start screen shortcuts.";
1326 // Shortcut filters for BatchShortcutAction().
1328 typedef base::Callback
<bool(const base::FilePath
& /*shortcut_path*/,
1329 const base::string16
& /*args*/)>
1330 ShortcutFilterCallback
;
1332 // FilterTargetEq is a shortcut filter that matches only shortcuts that have a
1333 // specific target, and optionally matches shortcuts that have non-empty
1335 class FilterTargetEq
{
1337 FilterTargetEq(const base::FilePath
& desired_target_exe
, bool require_args
);
1339 // Returns true if filter rules are satisfied, i.e.:
1340 // - |target_path|'s target == |desired_target_compare_|, and
1341 // - |args| is non-empty (if |require_args_| == true).
1342 bool Match(const base::FilePath
& target_path
,
1343 const base::string16
& args
) const;
1345 // A convenience routine to create a callback to call Match().
1346 // The callback is only valid during the lifetime of the FilterTargetEq
1348 ShortcutFilterCallback
AsShortcutFilterCallback();
1351 InstallUtil::ProgramCompare desired_target_compare_
;
1356 FilterTargetEq::FilterTargetEq(const base::FilePath
& desired_target_exe
,
1358 : desired_target_compare_(desired_target_exe
),
1359 require_args_(require_args
) {}
1361 bool FilterTargetEq::Match(const base::FilePath
& target_path
,
1362 const base::string16
& args
) const {
1363 if (!desired_target_compare_
.EvaluatePath(target_path
))
1365 if (require_args_
&& args
.empty())
1370 ShortcutFilterCallback
FilterTargetEq::AsShortcutFilterCallback() {
1371 return base::Bind(&FilterTargetEq::Match
, base::Unretained(this));
1374 // Shortcut operations for BatchShortcutAction().
1376 typedef base::Callback
<bool(const base::FilePath
& /*shortcut_path*/)>
1377 ShortcutOperationCallback
;
1379 bool ShortcutOpUnpin(const base::FilePath
& shortcut_path
) {
1380 VLOG(1) << "Trying to unpin " << shortcut_path
.value();
1381 if (!base::win::TaskbarUnpinShortcutLink(shortcut_path
.value().c_str())) {
1382 VLOG(1) << shortcut_path
.value() << " wasn't pinned (or the unpin failed).";
1383 // No error, since shortcut might not be pinned.
1388 bool ShortcutOpDelete(const base::FilePath
& shortcut_path
) {
1389 bool ret
= base::DeleteFile(shortcut_path
, false);
1390 LOG_IF(ERROR
, !ret
) << "Failed to remove " << shortcut_path
.value();
1394 bool ShortcutOpRetarget(const base::FilePath
& old_target
,
1395 const base::FilePath
& new_target
,
1396 const base::FilePath
& shortcut_path
) {
1397 base::win::ShortcutProperties new_prop
;
1398 new_prop
.set_target(new_target
);
1400 // If the old icon matches old target, then update icon while keeping the old
1401 // icon index. Non-fatal if we fail to get the old icon.
1402 base::win::ShortcutProperties old_prop
;
1403 if (base::win::ResolveShortcutProperties(
1405 base::win::ShortcutProperties::PROPERTIES_ICON
,
1407 if (InstallUtil::ProgramCompare(old_target
).EvaluatePath(old_prop
.icon
))
1408 new_prop
.set_icon(new_target
, old_prop
.icon_index
);
1410 LOG(ERROR
) << "Failed to resolve " << shortcut_path
.value();
1413 bool result
= base::win::CreateOrUpdateShortcutLink(
1414 shortcut_path
, new_prop
, base::win::SHORTCUT_UPDATE_EXISTING
);
1415 LOG_IF(ERROR
, !result
) << "Failed to retarget " << shortcut_path
.value();
1419 bool ShortcutOpListOrRemoveUnknownArgs(
1421 std::vector
<std::pair
<base::FilePath
, base::string16
> >* shortcuts
,
1422 const base::FilePath
& shortcut_path
) {
1423 base::string16 args
;
1424 if (!base::win::ResolveShortcut(shortcut_path
, NULL
, &args
))
1427 base::CommandLine
current_args(base::CommandLine::FromString(
1428 base::StringPrintf(L
"unused_program %ls", args
.c_str())));
1429 const char* const kept_switches
[] = {
1432 switches::kShowAppList
,
1433 switches::kProfileDirectory
,
1435 base::CommandLine
desired_args(base::CommandLine::NO_PROGRAM
);
1436 desired_args
.CopySwitchesFrom(current_args
, kept_switches
,
1437 arraysize(kept_switches
));
1438 if (desired_args
.argv().size() == current_args
.argv().size())
1441 shortcuts
->push_back(std::make_pair(shortcut_path
, args
));
1444 base::win::ShortcutProperties updated_properties
;
1445 updated_properties
.set_arguments(desired_args
.GetArgumentsString());
1446 return base::win::CreateOrUpdateShortcutLink(
1447 shortcut_path
, updated_properties
, base::win::SHORTCUT_UPDATE_EXISTING
);
1450 // {|location|, |dist|, |level|} determine |shortcut_folder|.
1451 // For each shortcut in |shortcut_folder| that match |shortcut_filter|, apply
1452 // |shortcut_operation|. Returns true if all operations are successful.
1453 // All intended operations are attempted, even if failures occur.
1454 // This method will abort and return false if |cancel| is non-NULL and gets set
1455 // at any point during this call.
1456 bool BatchShortcutAction(
1457 const ShortcutFilterCallback
& shortcut_filter
,
1458 const ShortcutOperationCallback
& shortcut_operation
,
1459 ShellUtil::ShortcutLocation location
,
1460 BrowserDistribution
* dist
,
1461 ShellUtil::ShellChange level
,
1462 const scoped_refptr
<ShellUtil::SharedCancellationFlag
>& cancel
) {
1463 DCHECK(!shortcut_operation
.is_null());
1465 // There is no system-level Quick Launch shortcut folder.
1466 if (level
== ShellUtil::SYSTEM_LEVEL
&&
1467 location
== ShellUtil::SHORTCUT_LOCATION_QUICK_LAUNCH
) {
1471 base::FilePath shortcut_folder
;
1472 if (!ShellUtil::GetShortcutPath(location
, dist
, level
, &shortcut_folder
)) {
1473 LOG(WARNING
) << "Cannot find path at location " << location
;
1477 bool success
= true;
1478 base::FileEnumerator
enumerator(
1479 shortcut_folder
, false, base::FileEnumerator::FILES
,
1480 base::string16(L
"*") + installer::kLnkExt
);
1481 base::FilePath target_path
;
1482 base::string16 args
;
1483 for (base::FilePath shortcut_path
= enumerator
.Next();
1484 !shortcut_path
.empty();
1485 shortcut_path
= enumerator
.Next()) {
1486 if (cancel
.get() && cancel
->data
.IsSet())
1488 if (base::win::ResolveShortcut(shortcut_path
, &target_path
, &args
)) {
1489 if (shortcut_filter
.Run(target_path
, args
) &&
1490 !shortcut_operation
.Run(shortcut_path
)) {
1494 LOG(ERROR
) << "Cannot resolve shortcut at " << shortcut_path
.value();
1502 // If the folder specified by {|location|, |dist|, |level|} is empty, remove it.
1503 // Otherwise do nothing. Returns true on success, including the vacuous case
1504 // where no deletion occurred because directory is non-empty.
1505 bool RemoveShortcutFolderIfEmpty(ShellUtil::ShortcutLocation location
,
1506 BrowserDistribution
* dist
,
1507 ShellUtil::ShellChange level
) {
1508 // Explicitly whitelist locations, since accidental calls can be very harmful.
1509 if (location
!= ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR
&&
1510 location
!= ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR
&&
1511 location
!= ShellUtil::SHORTCUT_LOCATION_APP_SHORTCUTS
) {
1516 base::FilePath shortcut_folder
;
1517 if (!ShellUtil::GetShortcutPath(location
, dist
, level
, &shortcut_folder
)) {
1518 LOG(WARNING
) << "Cannot find path at location " << location
;
1521 if (base::IsDirectoryEmpty(shortcut_folder
) &&
1522 !base::DeleteFile(shortcut_folder
, true)) {
1523 LOG(ERROR
) << "Cannot remove folder " << shortcut_folder
.value();
1531 const wchar_t* ShellUtil::kRegDefaultIcon
= L
"\\DefaultIcon";
1532 const wchar_t* ShellUtil::kRegShellPath
= L
"\\shell";
1533 const wchar_t* ShellUtil::kRegShellOpen
= L
"\\shell\\open\\command";
1534 const wchar_t* ShellUtil::kRegStartMenuInternet
=
1535 L
"Software\\Clients\\StartMenuInternet";
1536 const wchar_t* ShellUtil::kRegClasses
= L
"Software\\Classes";
1537 const wchar_t* ShellUtil::kRegRegisteredApplications
=
1538 L
"Software\\RegisteredApplications";
1539 const wchar_t* ShellUtil::kRegVistaUrlPrefs
=
1540 L
"Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
1541 L
"http\\UserChoice";
1542 const wchar_t* ShellUtil::kAppPathsRegistryKey
=
1543 L
"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths";
1544 const wchar_t* ShellUtil::kAppPathsRegistryPathName
= L
"Path";
1546 const wchar_t* ShellUtil::kDefaultFileAssociations
[] = {L
".htm", L
".html",
1547 L
".shtml", L
".xht", L
".xhtml", NULL
};
1548 const wchar_t* ShellUtil::kPotentialFileAssociations
[] = {L
".htm", L
".html",
1549 L
".shtml", L
".xht", L
".xhtml", L
".webp", NULL
};
1550 const wchar_t* ShellUtil::kBrowserProtocolAssociations
[] = {L
"ftp", L
"http",
1552 const wchar_t* ShellUtil::kPotentialProtocolAssociations
[] = {L
"ftp", L
"http",
1553 L
"https", L
"irc", L
"mailto", L
"mms", L
"news", L
"nntp", L
"sms", L
"smsto",
1554 L
"tel", L
"urn", L
"webcal", NULL
};
1555 const wchar_t* ShellUtil::kRegUrlProtocol
= L
"URL Protocol";
1556 const wchar_t* ShellUtil::kRegApplication
= L
"\\Application";
1557 const wchar_t* ShellUtil::kRegAppUserModelId
= L
"AppUserModelId";
1558 const wchar_t* ShellUtil::kRegApplicationDescription
=
1559 L
"ApplicationDescription";
1560 const wchar_t* ShellUtil::kRegApplicationName
= L
"ApplicationName";
1561 const wchar_t* ShellUtil::kRegApplicationIcon
= L
"ApplicationIcon";
1562 const wchar_t* ShellUtil::kRegApplicationCompany
= L
"ApplicationCompany";
1563 const wchar_t* ShellUtil::kRegExePath
= L
"\\.exe";
1564 const wchar_t* ShellUtil::kRegVerbOpen
= L
"open";
1565 const wchar_t* ShellUtil::kRegVerbOpenNewWindow
= L
"opennewwindow";
1566 const wchar_t* ShellUtil::kRegVerbRun
= L
"run";
1567 const wchar_t* ShellUtil::kRegCommand
= L
"command";
1568 const wchar_t* ShellUtil::kRegDelegateExecute
= L
"DelegateExecute";
1569 const wchar_t* ShellUtil::kRegOpenWithProgids
= L
"OpenWithProgids";
1571 bool ShellUtil::QuickIsChromeRegisteredInHKLM(BrowserDistribution
* dist
,
1572 const base::FilePath
& chrome_exe
,
1573 const base::string16
& suffix
) {
1574 return QuickIsChromeRegistered(dist
, chrome_exe
, suffix
,
1575 CONFIRM_SHELL_REGISTRATION_IN_HKLM
);
1578 bool ShellUtil::ShortcutLocationIsSupported(
1579 ShellUtil::ShortcutLocation location
) {
1581 case SHORTCUT_LOCATION_DESKTOP
: // Falls through.
1582 case SHORTCUT_LOCATION_QUICK_LAUNCH
: // Falls through.
1583 case SHORTCUT_LOCATION_START_MENU_ROOT
: // Falls through.
1584 case SHORTCUT_LOCATION_START_MENU_CHROME_DIR
: // Falls through.
1585 case SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR
:
1587 case SHORTCUT_LOCATION_TASKBAR_PINS
:
1588 return base::win::GetVersion() >= base::win::VERSION_WIN7
;
1589 case SHORTCUT_LOCATION_APP_SHORTCUTS
:
1590 return base::win::GetVersion() >= base::win::VERSION_WIN8
;
1597 bool ShellUtil::GetShortcutPath(ShellUtil::ShortcutLocation location
,
1598 BrowserDistribution
* dist
,
1600 base::FilePath
* path
) {
1603 base::string16 folder_to_append
;
1605 case SHORTCUT_LOCATION_DESKTOP
:
1606 dir_key
= (level
== CURRENT_USER
) ? base::DIR_USER_DESKTOP
:
1607 base::DIR_COMMON_DESKTOP
;
1609 case SHORTCUT_LOCATION_QUICK_LAUNCH
:
1610 // There is no support for a system-level Quick Launch shortcut.
1611 DCHECK_EQ(level
, CURRENT_USER
);
1612 dir_key
= base::DIR_USER_QUICK_LAUNCH
;
1614 case SHORTCUT_LOCATION_START_MENU_ROOT
:
1615 dir_key
= (level
== CURRENT_USER
) ? base::DIR_START_MENU
:
1616 base::DIR_COMMON_START_MENU
;
1618 case SHORTCUT_LOCATION_START_MENU_CHROME_DIR
:
1619 dir_key
= (level
== CURRENT_USER
) ? base::DIR_START_MENU
:
1620 base::DIR_COMMON_START_MENU
;
1621 folder_to_append
= dist
->GetStartMenuShortcutSubfolder(
1622 BrowserDistribution::SUBFOLDER_CHROME
);
1624 case SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR
:
1625 dir_key
= (level
== CURRENT_USER
) ? base::DIR_START_MENU
:
1626 base::DIR_COMMON_START_MENU
;
1627 folder_to_append
= dist
->GetStartMenuShortcutSubfolder(
1628 BrowserDistribution::SUBFOLDER_APPS
);
1630 case SHORTCUT_LOCATION_TASKBAR_PINS
:
1631 dir_key
= base::DIR_TASKBAR_PINS
;
1633 case SHORTCUT_LOCATION_APP_SHORTCUTS
:
1634 // TODO(huangs): Move GetAppShortcutsFolder() logic into base_paths_win.
1635 return GetAppShortcutsFolder(dist
, level
, path
);
1642 if (!PathService::Get(dir_key
, path
) || path
->empty()) {
1643 NOTREACHED() << dir_key
;
1647 if (!folder_to_append
.empty())
1648 *path
= path
->Append(folder_to_append
);
1653 bool ShellUtil::CreateOrUpdateShortcut(
1654 ShellUtil::ShortcutLocation location
,
1655 BrowserDistribution
* dist
,
1656 const ShellUtil::ShortcutProperties
& properties
,
1657 ShellUtil::ShortcutOperation operation
) {
1658 // Explicitly whitelist locations to which this is applicable.
1659 if (location
!= SHORTCUT_LOCATION_DESKTOP
&&
1660 location
!= SHORTCUT_LOCATION_QUICK_LAUNCH
&&
1661 location
!= SHORTCUT_LOCATION_START_MENU_ROOT
&&
1662 location
!= SHORTCUT_LOCATION_START_MENU_CHROME_DIR
&&
1663 location
!= SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR
) {
1669 // |pin_to_taskbar| is only acknowledged when first creating the shortcut.
1670 DCHECK(!properties
.pin_to_taskbar
||
1671 operation
== SHELL_SHORTCUT_CREATE_ALWAYS
||
1672 operation
== SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL
);
1674 base::FilePath user_shortcut_path
;
1675 base::FilePath system_shortcut_path
;
1676 if (location
== SHORTCUT_LOCATION_QUICK_LAUNCH
) {
1677 // There is no system-level shortcut for Quick Launch.
1678 DCHECK_EQ(properties
.level
, CURRENT_USER
);
1679 } else if (!GetShortcutPath(
1680 location
, dist
, SYSTEM_LEVEL
, &system_shortcut_path
)) {
1685 base::string16
shortcut_name(
1686 ExtractShortcutNameFromProperties(dist
, properties
));
1687 system_shortcut_path
= system_shortcut_path
.Append(shortcut_name
);
1689 base::FilePath
* chosen_path
;
1690 bool should_install_shortcut
= true;
1691 if (properties
.level
== SYSTEM_LEVEL
) {
1692 // Install the system-level shortcut if requested.
1693 chosen_path
= &system_shortcut_path
;
1694 } else if (operation
!= SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL
||
1695 system_shortcut_path
.empty() ||
1696 !base::PathExists(system_shortcut_path
)) {
1697 // Otherwise install the user-level shortcut, unless the system-level
1698 // variant of this shortcut is present on the machine and |operation| states
1699 // not to create a user-level shortcut in that case.
1700 if (!GetShortcutPath(location
, dist
, CURRENT_USER
, &user_shortcut_path
)) {
1704 user_shortcut_path
= user_shortcut_path
.Append(shortcut_name
);
1705 chosen_path
= &user_shortcut_path
;
1707 // Do not install any shortcut if we are told to install a user-level
1708 // shortcut, but the system-level variant of that shortcut is present.
1709 // Other actions (e.g., pinning) can still happen with respect to the
1710 // existing system-level shortcut however.
1711 chosen_path
= &system_shortcut_path
;
1712 should_install_shortcut
= false;
1715 if (chosen_path
== NULL
|| chosen_path
->empty()) {
1720 base::win::ShortcutOperation shortcut_operation
=
1721 TranslateShortcutOperation(operation
);
1723 if (should_install_shortcut
) {
1724 // Make sure the parent directories exist when creating the shortcut.
1725 if (shortcut_operation
== base::win::SHORTCUT_CREATE_ALWAYS
&&
1726 !base::CreateDirectory(chosen_path
->DirName())) {
1731 base::win::ShortcutProperties
shortcut_properties(
1732 TranslateShortcutProperties(properties
));
1733 ret
= base::win::CreateOrUpdateShortcutLink(
1734 *chosen_path
, shortcut_properties
, shortcut_operation
);
1737 if (ret
&& shortcut_operation
== base::win::SHORTCUT_CREATE_ALWAYS
&&
1738 properties
.pin_to_taskbar
&&
1739 base::win::GetVersion() >= base::win::VERSION_WIN7
) {
1740 ret
= base::win::TaskbarPinShortcutLink(chosen_path
->value().c_str());
1742 LOG(ERROR
) << "Failed to pin " << chosen_path
->value();
1749 base::string16
ShellUtil::FormatIconLocation(const base::FilePath
& icon_path
,
1751 base::string16
icon_string(icon_path
.value());
1752 icon_string
.append(L
",");
1753 icon_string
.append(base::IntToString16(icon_index
));
1757 base::string16
ShellUtil::GetChromeShellOpenCmd(
1758 const base::FilePath
& chrome_exe
) {
1759 return L
"\"" + chrome_exe
.value() + L
"\" -- \"%1\"";
1762 base::string16
ShellUtil::GetChromeDelegateCommand(
1763 const base::FilePath
& chrome_exe
) {
1764 return L
"\"" + chrome_exe
.value() + L
"\" -- %*";
1767 void ShellUtil::GetRegisteredBrowsers(
1768 BrowserDistribution
* dist
,
1769 std::map
<base::string16
, base::string16
>* browsers
) {
1773 const base::string16
base_key(ShellUtil::kRegStartMenuInternet
);
1774 base::string16 client_path
;
1776 base::string16 name
;
1777 base::string16 command
;
1779 // HKCU has precedence over HKLM for these registrations: http://goo.gl/xjczJ.
1780 // Look in HKCU second to override any identical values found in HKLM.
1781 const HKEY roots
[] = { HKEY_LOCAL_MACHINE
, HKEY_CURRENT_USER
};
1782 for (int i
= 0; i
< arraysize(roots
); ++i
) {
1783 const HKEY root
= roots
[i
];
1784 for (base::win::RegistryKeyIterator
iter(root
, base_key
.c_str());
1785 iter
.Valid(); ++iter
) {
1786 client_path
.assign(base_key
).append(1, L
'\\').append(iter
.Name());
1787 // Read the browser's name (localized according to install language).
1788 if (key
.Open(root
, client_path
.c_str(),
1789 KEY_QUERY_VALUE
) != ERROR_SUCCESS
||
1790 key
.ReadValue(NULL
, &name
) != ERROR_SUCCESS
||
1792 name
.find(dist
->GetBaseAppName()) != base::string16::npos
) {
1795 // Read the browser's reinstall command.
1796 if (key
.Open(root
, (client_path
+ L
"\\InstallInfo").c_str(),
1797 KEY_QUERY_VALUE
) == ERROR_SUCCESS
&&
1798 key
.ReadValue(kReinstallCommand
, &command
) == ERROR_SUCCESS
&&
1800 (*browsers
)[name
] = command
;
1806 base::string16
ShellUtil::GetCurrentInstallationSuffix(
1807 BrowserDistribution
* dist
,
1808 const base::FilePath
& chrome_exe
) {
1809 // This method is somewhat the opposite of GetInstallationSpecificSuffix().
1810 // In this case we are not trying to determine the current suffix for the
1811 // upcoming installation (i.e. not trying to stick to a currently bad
1812 // registration style if one is present).
1813 // Here we want to determine which suffix we should use at run-time.
1814 // In order of preference, we prefer (for user-level installs):
1815 // 1) Base 32 encoding of the md5 hash of the user's sid (new-style).
1816 // 2) Username (old-style).
1817 // 3) Unsuffixed (even worse).
1818 base::string16 tested_suffix
;
1819 if (InstallUtil::IsPerUserInstall(chrome_exe
) &&
1820 (!GetUserSpecificRegistrySuffix(&tested_suffix
) ||
1821 !QuickIsChromeRegistered(dist
, chrome_exe
, tested_suffix
,
1822 CONFIRM_PROGID_REGISTRATION
)) &&
1823 (!GetOldUserSpecificRegistrySuffix(&tested_suffix
) ||
1824 !QuickIsChromeRegistered(dist
, chrome_exe
, tested_suffix
,
1825 CONFIRM_PROGID_REGISTRATION
)) &&
1826 !QuickIsChromeRegistered(dist
, chrome_exe
, tested_suffix
.erase(),
1827 CONFIRM_PROGID_REGISTRATION
)) {
1828 // If Chrome is not registered under any of the possible suffixes (e.g.
1829 // tests, Canary, etc.): use the new-style suffix at run-time.
1830 if (!GetUserSpecificRegistrySuffix(&tested_suffix
))
1833 return tested_suffix
;
1836 base::string16
ShellUtil::GetApplicationName(BrowserDistribution
* dist
,
1837 const base::FilePath
& chrome_exe
) {
1838 base::string16 app_name
= dist
->GetBaseAppName();
1839 app_name
+= GetCurrentInstallationSuffix(dist
, chrome_exe
);
1843 base::string16
ShellUtil::GetBrowserModelId(BrowserDistribution
* dist
,
1844 bool is_per_user_install
) {
1845 base::string16
app_id(dist
->GetBaseAppId());
1846 base::string16 suffix
;
1848 // TODO(robertshield): Temporary hack to make the kRegisterChromeBrowserSuffix
1849 // apply to all registry values computed down in these murky depths.
1850 base::CommandLine
& command_line
= *base::CommandLine::ForCurrentProcess();
1851 if (command_line
.HasSwitch(
1852 installer::switches::kRegisterChromeBrowserSuffix
)) {
1853 suffix
= command_line
.GetSwitchValueNative(
1854 installer::switches::kRegisterChromeBrowserSuffix
);
1855 } else if (is_per_user_install
&& !GetUserSpecificRegistrySuffix(&suffix
)) {
1858 // There is only one component (i.e. the suffixed appid) in this case, but it
1859 // is still necessary to go through the appid constructor to make sure the
1860 // returned appid is truncated if necessary.
1861 std::vector
<base::string16
> components(1, app_id
.append(suffix
));
1862 return BuildAppModelId(components
);
1865 base::string16
ShellUtil::BuildAppModelId(
1866 const std::vector
<base::string16
>& components
) {
1867 DCHECK_GT(components
.size(), 0U);
1869 // Find the maximum numbers of characters allowed in each component
1870 // (accounting for the dots added between each component).
1871 const size_t available_chars
=
1872 installer::kMaxAppModelIdLength
- (components
.size() - 1);
1873 const size_t max_component_length
= available_chars
/ components
.size();
1875 // |max_component_length| should be at least 2; otherwise the truncation logic
1877 if (max_component_length
< 2U) {
1879 return (*components
.begin()).substr(0, installer::kMaxAppModelIdLength
);
1882 base::string16 app_id
;
1883 app_id
.reserve(installer::kMaxAppModelIdLength
);
1884 for (std::vector
<base::string16
>::const_iterator it
= components
.begin();
1885 it
!= components
.end(); ++it
) {
1886 if (it
!= components
.begin())
1887 app_id
.push_back(L
'.');
1889 const base::string16
& component
= *it
;
1890 DCHECK(!component
.empty());
1891 if (component
.length() > max_component_length
) {
1892 // Append a shortened version of this component. Cut in the middle to try
1893 // to avoid losing the unique parts of this component (which are usually
1894 // at the beginning or end for things like usernames and paths).
1895 app_id
.append(component
.c_str(), 0, max_component_length
/ 2);
1896 app_id
.append(component
.c_str(),
1897 component
.length() - ((max_component_length
+ 1) / 2),
1898 base::string16::npos
);
1900 app_id
.append(component
);
1903 // No spaces are allowed in the AppUserModelId according to MSDN.
1904 base::ReplaceChars(app_id
, base::ASCIIToUTF16(" "), base::ASCIIToUTF16("_"),
1909 ShellUtil::DefaultState
ShellUtil::GetChromeDefaultState() {
1910 base::FilePath app_path
;
1911 if (!PathService::Get(base::FILE_EXE
, &app_path
)) {
1913 return ShellUtil::UNKNOWN_DEFAULT
;
1916 return GetChromeDefaultStateFromPath(app_path
);
1919 ShellUtil::DefaultState
ShellUtil::GetChromeDefaultStateFromPath(
1920 const base::FilePath
& chrome_exe
) {
1921 BrowserDistribution
* distribution
= BrowserDistribution::GetDistribution();
1922 if (distribution
->GetDefaultBrowserControlPolicy() ==
1923 BrowserDistribution::DEFAULT_BROWSER_UNSUPPORTED
) {
1926 // When we check for default browser we don't necessarily want to count file
1927 // type handlers and icons as having changed the default browser status,
1928 // since the user may have changed their shell settings to cause HTML files
1929 // to open with a text editor for example. We also don't want to aggressively
1930 // claim FTP, since the user may have a separate FTP client. It is an open
1931 // question as to how to "heal" these settings. Perhaps the user should just
1932 // re-run the installer or run with the --set-default-browser command line
1933 // flag. There is doubtless some other key we can hook into to cause "Repair"
1934 // to show up in Add/Remove programs for us.
1935 static const wchar_t* const kChromeProtocols
[] = { L
"http", L
"https" };
1936 return ProbeProtocolHandlers(chrome_exe
,
1938 arraysize(kChromeProtocols
));
1941 ShellUtil::DefaultState
ShellUtil::GetChromeDefaultProtocolClientState(
1942 const base::string16
& protocol
) {
1943 BrowserDistribution
* distribution
= BrowserDistribution::GetDistribution();
1944 if (distribution
->GetDefaultBrowserControlPolicy() ==
1945 BrowserDistribution::DEFAULT_BROWSER_UNSUPPORTED
) {
1949 if (protocol
.empty())
1950 return UNKNOWN_DEFAULT
;
1952 base::FilePath chrome_exe
;
1953 if (!PathService::Get(base::FILE_EXE
, &chrome_exe
)) {
1955 return ShellUtil::UNKNOWN_DEFAULT
;
1958 const wchar_t* const protocols
[] = { protocol
.c_str() };
1959 return ProbeProtocolHandlers(chrome_exe
,
1961 arraysize(protocols
));
1965 bool ShellUtil::CanMakeChromeDefaultUnattended() {
1966 return base::win::GetVersion() < base::win::VERSION_WIN8
;
1969 bool ShellUtil::MakeChromeDefault(BrowserDistribution
* dist
,
1971 const base::FilePath
& chrome_exe
,
1972 bool elevate_if_not_admin
) {
1973 DCHECK(!(shell_change
& ShellUtil::SYSTEM_LEVEL
) || IsUserAnAdmin());
1975 BrowserDistribution
* distribution
= BrowserDistribution::GetDistribution();
1976 if (distribution
->GetDefaultBrowserControlPolicy() !=
1977 BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL
) {
1981 // Windows 8 does not permit making a browser default just like that.
1982 // This process needs to be routed through the system's UI. Use
1983 // ShowMakeChromeDefaultSystemUI instead (below).
1984 if (!CanMakeChromeDefaultUnattended()) {
1988 if (!ShellUtil::RegisterChromeBrowser(
1989 dist
, chrome_exe
, base::string16(), elevate_if_not_admin
)) {
1994 // First use the new "recommended" way on Vista to make Chrome default
1996 base::string16 app_name
= GetApplicationName(dist
, chrome_exe
);
1998 if (base::win::GetVersion() >= base::win::VERSION_VISTA
) {
1999 // On Windows Vista and Win7 we still can set ourselves via the
2000 // the IApplicationAssociationRegistration interface.
2001 VLOG(1) << "Registering Chrome as default browser on Vista.";
2002 base::win::ScopedComPtr
<IApplicationAssociationRegistration
> pAAR
;
2003 HRESULT hr
= pAAR
.CreateInstance(CLSID_ApplicationAssociationRegistration
,
2004 NULL
, CLSCTX_INPROC
);
2005 if (SUCCEEDED(hr
)) {
2006 for (int i
= 0; ShellUtil::kBrowserProtocolAssociations
[i
] != NULL
; i
++) {
2007 hr
= pAAR
->SetAppAsDefault(app_name
.c_str(),
2008 ShellUtil::kBrowserProtocolAssociations
[i
], AT_URLPROTOCOL
);
2009 if (!SUCCEEDED(hr
)) {
2011 LOG(ERROR
) << "Failed to register as default for protocol "
2012 << ShellUtil::kBrowserProtocolAssociations
[i
]
2013 << " (" << hr
<< ")";
2017 for (int i
= 0; ShellUtil::kDefaultFileAssociations
[i
] != NULL
; i
++) {
2018 hr
= pAAR
->SetAppAsDefault(app_name
.c_str(),
2019 ShellUtil::kDefaultFileAssociations
[i
], AT_FILEEXTENSION
);
2020 if (!SUCCEEDED(hr
)) {
2022 LOG(ERROR
) << "Failed to register as default for file extension "
2023 << ShellUtil::kDefaultFileAssociations
[i
]
2024 << " (" << hr
<< ")";
2030 if (!RegisterChromeAsDefaultXPStyle(dist
, shell_change
, chrome_exe
))
2033 // Send Windows notification event so that it can update icons for
2034 // file associations.
2035 SHChangeNotify(SHCNE_ASSOCCHANGED
, SHCNF_IDLIST
, NULL
, NULL
);
2039 bool ShellUtil::ShowMakeChromeDefaultSystemUI(
2040 BrowserDistribution
* dist
,
2041 const base::FilePath
& chrome_exe
) {
2042 DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8
);
2043 if (dist
->GetDefaultBrowserControlPolicy() !=
2044 BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL
) {
2048 if (!RegisterChromeBrowser(dist
, chrome_exe
, base::string16(), true))
2051 bool succeeded
= true;
2052 bool is_default
= (GetChromeDefaultState() == IS_DEFAULT
);
2054 // On Windows 8, you can't set yourself as the default handler
2055 // programatically. In other words IApplicationAssociationRegistration
2056 // has been rendered useless. What you can do is to launch
2057 // "Set Program Associations" section of the "Default Programs"
2058 // control panel, which is a mess, or pop the concise "How you want to open
2059 // webpages?" dialog. We choose the latter.
2060 succeeded
= LaunchSelectDefaultProtocolHandlerDialog(L
"http");
2061 is_default
= (succeeded
&& GetChromeDefaultState() == IS_DEFAULT
);
2063 if (succeeded
&& is_default
)
2064 RegisterChromeAsDefaultXPStyle(dist
, CURRENT_USER
, chrome_exe
);
2068 bool ShellUtil::MakeChromeDefaultProtocolClient(
2069 BrowserDistribution
* dist
,
2070 const base::FilePath
& chrome_exe
,
2071 const base::string16
& protocol
) {
2072 if (dist
->GetDefaultBrowserControlPolicy() !=
2073 BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL
) {
2077 if (!RegisterChromeForProtocol(
2078 dist
, chrome_exe
, base::string16(), protocol
, true))
2081 // Windows 8 does not permit making a browser default just like that.
2082 // This process needs to be routed through the system's UI. Use
2083 // ShowMakeChromeDefaultProocolClientSystemUI instead (below).
2084 if (!CanMakeChromeDefaultUnattended())
2088 // First use the new "recommended" way on Vista to make Chrome default
2089 // protocol handler.
2090 if (base::win::GetVersion() >= base::win::VERSION_VISTA
) {
2091 VLOG(1) << "Registering Chrome as default handler for " << protocol
2093 base::win::ScopedComPtr
<IApplicationAssociationRegistration
> pAAR
;
2094 HRESULT hr
= pAAR
.CreateInstance(CLSID_ApplicationAssociationRegistration
,
2095 NULL
, CLSCTX_INPROC
);
2096 if (SUCCEEDED(hr
)) {
2097 base::string16 app_name
= GetApplicationName(dist
, chrome_exe
);
2098 hr
= pAAR
->SetAppAsDefault(app_name
.c_str(), protocol
.c_str(),
2101 if (!SUCCEEDED(hr
)) {
2103 LOG(ERROR
) << "Could not make Chrome default protocol client (Vista):"
2104 << " HRESULT=" << hr
<< ".";
2108 // Now use the old way to associate Chrome with the desired protocol. This
2109 // should not be required on Vista+, but since some applications still read
2110 // Software\Classes\<protocol> key directly, do this on Vista+ also.
2111 if (!RegisterChromeAsDefaultProtocolClientXPStyle(dist
, chrome_exe
, protocol
))
2117 bool ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(
2118 BrowserDistribution
* dist
,
2119 const base::FilePath
& chrome_exe
,
2120 const base::string16
& protocol
) {
2121 DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8
);
2122 if (dist
->GetDefaultBrowserControlPolicy() !=
2123 BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL
) {
2127 if (!RegisterChromeForProtocol(
2128 dist
, chrome_exe
, base::string16(), protocol
, true))
2131 bool succeeded
= true;
2133 GetChromeDefaultProtocolClientState(protocol
) == IS_DEFAULT
);
2135 // On Windows 8, you can't set yourself as the default handler
2136 // programatically. In other words IApplicationAssociationRegistration
2137 // has been rendered useless. What you can do is to launch
2138 // "Set Program Associations" section of the "Default Programs"
2139 // control panel, which is a mess, or pop the concise "How you want to open
2140 // links of this type (protocol)?" dialog. We choose the latter.
2141 succeeded
= LaunchSelectDefaultProtocolHandlerDialog(protocol
.c_str());
2142 is_default
= (succeeded
&&
2143 GetChromeDefaultProtocolClientState(protocol
) == IS_DEFAULT
);
2145 if (succeeded
&& is_default
)
2146 RegisterChromeAsDefaultProtocolClientXPStyle(dist
, chrome_exe
, protocol
);
2150 bool ShellUtil::RegisterChromeBrowser(BrowserDistribution
* dist
,
2151 const base::FilePath
& chrome_exe
,
2152 const base::string16
& unique_suffix
,
2153 bool elevate_if_not_admin
) {
2154 if (dist
->GetDefaultBrowserControlPolicy() ==
2155 BrowserDistribution::DEFAULT_BROWSER_UNSUPPORTED
) {
2159 base::CommandLine
& command_line
= *base::CommandLine::ForCurrentProcess();
2161 base::string16 suffix
;
2162 if (!unique_suffix
.empty()) {
2163 suffix
= unique_suffix
;
2164 } else if (command_line
.HasSwitch(
2165 installer::switches::kRegisterChromeBrowserSuffix
)) {
2166 suffix
= command_line
.GetSwitchValueNative(
2167 installer::switches::kRegisterChromeBrowserSuffix
);
2168 } else if (!GetInstallationSpecificSuffix(dist
, chrome_exe
, &suffix
)) {
2172 RemoveRunVerbOnWindows8(dist
, chrome_exe
);
2174 bool user_level
= InstallUtil::IsPerUserInstall(chrome_exe
);
2175 HKEY root
= DetermineRegistrationRoot(user_level
);
2177 // Look only in HKLM for system-level installs (otherwise, if a user-level
2178 // install is also present, it will lead IsChromeRegistered() to think this
2179 // system-level install isn't registered properly as it is shadowed by the
2180 // user-level install's registrations).
2181 uint32 look_for_in
= user_level
?
2182 RegistryEntry::LOOK_IN_HKCU_THEN_HKLM
: RegistryEntry::LOOK_IN_HKLM
;
2184 // Check if chrome is already registered with this suffix.
2185 if (IsChromeRegistered(dist
, chrome_exe
, suffix
, look_for_in
))
2189 if (root
== HKEY_CURRENT_USER
|| IsUserAnAdmin()) {
2190 // Do the full registration if we can do it at user-level or if the user is
2192 ScopedVector
<RegistryEntry
> progid_and_appreg_entries
;
2193 ScopedVector
<RegistryEntry
> shell_entries
;
2194 RegistryEntry::GetChromeProgIdEntries(
2195 dist
, chrome_exe
, suffix
, &progid_and_appreg_entries
);
2196 RegistryEntry::GetChromeAppRegistrationEntries(
2197 chrome_exe
, suffix
, &progid_and_appreg_entries
);
2198 RegistryEntry::GetShellIntegrationEntries(
2199 dist
, chrome_exe
, suffix
, &shell_entries
);
2200 result
= (AddRegistryEntries(root
, progid_and_appreg_entries
) &&
2201 AddRegistryEntries(root
, shell_entries
));
2202 } else if (elevate_if_not_admin
&&
2203 base::win::GetVersion() >= base::win::VERSION_VISTA
&&
2204 ElevateAndRegisterChrome(dist
, chrome_exe
, suffix
, base::string16())) {
2205 // If the user is not an admin and OS is between Vista and Windows 7
2206 // inclusively, try to elevate and register. This is only intended for
2207 // user-level installs as system-level installs should always be run with
2211 // If we got to this point then all we can do is create ProgId and basic app
2212 // registrations under HKCU.
2213 ScopedVector
<RegistryEntry
> entries
;
2214 RegistryEntry::GetChromeProgIdEntries(
2215 dist
, chrome_exe
, base::string16(), &entries
);
2216 // Prefer to use |suffix|; unless Chrome's ProgIds are already registered
2217 // with no suffix (as per the old registration style): in which case some
2218 // other registry entries could refer to them and since we were not able to
2219 // set our HKLM entries above, we are better off not altering these here.
2220 if (!AreEntriesRegistered(entries
, RegistryEntry::LOOK_IN_HKCU
)) {
2221 if (!suffix
.empty()) {
2223 RegistryEntry::GetChromeProgIdEntries(
2224 dist
, chrome_exe
, suffix
, &entries
);
2225 RegistryEntry::GetChromeAppRegistrationEntries(
2226 chrome_exe
, suffix
, &entries
);
2228 result
= AddRegistryEntries(HKEY_CURRENT_USER
, entries
);
2230 // The ProgId is registered unsuffixed in HKCU, also register the app with
2231 // Windows in HKCU (this was not done in the old registration style and
2232 // thus needs to be done after the above check for the unsuffixed
2235 RegistryEntry::GetChromeAppRegistrationEntries(
2236 chrome_exe
, base::string16(), &entries
);
2237 result
= AddRegistryEntries(HKEY_CURRENT_USER
, entries
);
2240 SHChangeNotify(SHCNE_ASSOCCHANGED
, SHCNF_IDLIST
, NULL
, NULL
);
2244 bool ShellUtil::RegisterChromeForProtocol(BrowserDistribution
* dist
,
2245 const base::FilePath
& chrome_exe
,
2246 const base::string16
& unique_suffix
,
2247 const base::string16
& protocol
,
2248 bool elevate_if_not_admin
) {
2249 if (dist
->GetDefaultBrowserControlPolicy() ==
2250 BrowserDistribution::DEFAULT_BROWSER_UNSUPPORTED
) {
2254 base::string16 suffix
;
2255 if (!unique_suffix
.empty()) {
2256 suffix
= unique_suffix
;
2257 } else if (!GetInstallationSpecificSuffix(dist
, chrome_exe
, &suffix
)) {
2261 bool user_level
= InstallUtil::IsPerUserInstall(chrome_exe
);
2262 HKEY root
= DetermineRegistrationRoot(user_level
);
2264 // Look only in HKLM for system-level installs (otherwise, if a user-level
2265 // install is also present, it could lead IsChromeRegisteredForProtocol() to
2266 // think this system-level install isn't registered properly as it may be
2267 // shadowed by the user-level install's registrations).
2268 uint32 look_for_in
= user_level
?
2269 RegistryEntry::LOOK_IN_HKCU_THEN_HKLM
: RegistryEntry::LOOK_IN_HKLM
;
2271 // Check if chrome is already registered with this suffix.
2272 if (IsChromeRegisteredForProtocol(dist
, suffix
, protocol
, look_for_in
))
2275 if (root
== HKEY_CURRENT_USER
|| IsUserAnAdmin()) {
2276 // We can do this operation directly.
2277 // First, make sure Chrome is fully registered on this machine.
2278 if (!RegisterChromeBrowser(dist
, chrome_exe
, suffix
, false))
2281 // Write in the capabillity for the protocol.
2282 ScopedVector
<RegistryEntry
> entries
;
2283 RegistryEntry::GetProtocolCapabilityEntries(dist
, suffix
, protocol
,
2285 return AddRegistryEntries(root
, entries
);
2286 } else if (elevate_if_not_admin
&&
2287 base::win::GetVersion() >= base::win::VERSION_VISTA
) {
2288 // Elevate to do the whole job
2289 return ElevateAndRegisterChrome(dist
, chrome_exe
, suffix
, protocol
);
2291 // Admin rights are required to register capabilities before Windows 8.
2297 bool ShellUtil::RemoveShortcuts(ShellUtil::ShortcutLocation location
,
2298 BrowserDistribution
* dist
,
2300 const base::FilePath
& target_exe
) {
2301 if (!ShellUtil::ShortcutLocationIsSupported(location
))
2302 return true; // Vacuous success.
2304 FilterTargetEq
shortcut_filter(target_exe
, false);
2305 // Main operation to apply to each shortcut in the directory specified.
2306 ShortcutOperationCallback
shortcut_operation(
2307 location
== SHORTCUT_LOCATION_TASKBAR_PINS
?
2308 base::Bind(&ShortcutOpUnpin
) : base::Bind(&ShortcutOpDelete
));
2309 bool success
= BatchShortcutAction(shortcut_filter
.AsShortcutFilterCallback(),
2310 shortcut_operation
, location
, dist
, level
,
2312 // Remove chrome-specific shortcut folders if they are now empty.
2314 (location
== SHORTCUT_LOCATION_START_MENU_CHROME_DIR
||
2315 location
== SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR
||
2316 location
== SHORTCUT_LOCATION_APP_SHORTCUTS
)) {
2317 success
= RemoveShortcutFolderIfEmpty(location
, dist
, level
);
2323 bool ShellUtil::RetargetShortcutsWithArgs(
2324 ShellUtil::ShortcutLocation location
,
2325 BrowserDistribution
* dist
,
2327 const base::FilePath
& old_target_exe
,
2328 const base::FilePath
& new_target_exe
) {
2329 if (!ShellUtil::ShortcutLocationIsSupported(location
))
2330 return true; // Vacuous success.
2332 FilterTargetEq
shortcut_filter(old_target_exe
, true);
2333 ShortcutOperationCallback
shortcut_operation(
2334 base::Bind(&ShortcutOpRetarget
, old_target_exe
, new_target_exe
));
2335 return BatchShortcutAction(shortcut_filter
.AsShortcutFilterCallback(),
2336 shortcut_operation
, location
, dist
, level
, NULL
);
2340 bool ShellUtil::ShortcutListMaybeRemoveUnknownArgs(
2341 ShellUtil::ShortcutLocation location
,
2342 BrowserDistribution
* dist
,
2344 const base::FilePath
& chrome_exe
,
2346 const scoped_refptr
<SharedCancellationFlag
>& cancel
,
2347 std::vector
<std::pair
<base::FilePath
, base::string16
> >* shortcuts
) {
2348 if (!ShellUtil::ShortcutLocationIsSupported(location
))
2351 FilterTargetEq
shortcut_filter(chrome_exe
, true);
2352 ShortcutOperationCallback
shortcut_operation(
2353 base::Bind(&ShortcutOpListOrRemoveUnknownArgs
, do_removal
, shortcuts
));
2354 return BatchShortcutAction(shortcut_filter
.AsShortcutFilterCallback(),
2355 shortcut_operation
, location
, dist
, level
, cancel
);
2358 bool ShellUtil::GetUserSpecificRegistrySuffix(base::string16
* suffix
) {
2359 // Use a thread-safe cache for the user's suffix.
2360 static base::LazyInstance
<UserSpecificRegistrySuffix
>::Leaky suffix_instance
=
2361 LAZY_INSTANCE_INITIALIZER
;
2362 return suffix_instance
.Get().GetSuffix(suffix
);
2365 bool ShellUtil::GetOldUserSpecificRegistrySuffix(base::string16
* suffix
) {
2366 wchar_t user_name
[256];
2367 DWORD size
= arraysize(user_name
);
2368 if (::GetUserName(user_name
, &size
) == 0 || size
< 1) {
2372 suffix
->reserve(size
);
2373 suffix
->assign(1, L
'.');
2374 suffix
->append(user_name
, size
- 1);
2378 base::string16
ShellUtil::ByteArrayToBase32(const uint8
* bytes
, size_t size
) {
2379 static const char kEncoding
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
2381 // Eliminate special cases first.
2383 return base::string16();
2384 } else if (size
== 1) {
2386 ret
.push_back(kEncoding
[(bytes
[0] & 0xf8) >> 3]);
2387 ret
.push_back(kEncoding
[(bytes
[0] & 0x07) << 2]);
2389 } else if (size
>= std::numeric_limits
<size_t>::max() / 8) {
2390 // If |size| is too big, the calculation of |encoded_length| below will
2393 return base::string16();
2396 // Overestimate the number of bits in the string by 4 so that dividing by 5
2397 // is the equivalent of rounding up the actual number of bits divided by 5.
2398 const size_t encoded_length
= (size
* 8 + 4) / 5;
2401 ret
.reserve(encoded_length
);
2403 // A bit stream which will be read from the left and appended to from the
2404 // right as it's emptied.
2405 uint16 bit_stream
= (bytes
[0] << 8) + bytes
[1];
2406 size_t next_byte_index
= 2;
2408 while (free_bits
< 16) {
2409 // Extract the 5 leftmost bits in the stream
2410 ret
.push_back(kEncoding
[(bit_stream
& 0xf800) >> 11]);
2414 // If there is enough room in the bit stream, inject another byte (if there
2415 // are any left...).
2416 if (free_bits
>= 8 && next_byte_index
< size
) {
2418 bit_stream
+= bytes
[next_byte_index
++] << free_bits
;
2422 DCHECK_EQ(ret
.length(), encoded_length
);
2427 bool ShellUtil::AddFileAssociations(
2428 const base::string16
& prog_id
,
2429 const base::CommandLine
& command_line
,
2430 const base::string16
& file_type_name
,
2431 const base::FilePath
& icon_path
,
2432 const std::set
<base::string16
>& file_extensions
) {
2433 ScopedVector
<RegistryEntry
> entries
;
2435 // Create a class for this app.
2436 RegistryEntry::ApplicationInfo app_info
;
2437 app_info
.prog_id
= prog_id
;
2438 app_info
.file_type_name
= file_type_name
;
2439 app_info
.file_type_icon_path
= icon_path
;
2440 app_info
.file_type_icon_index
= 0;
2441 app_info
.command_line
= command_line
.GetCommandLineStringWithPlaceholders();
2442 RegistryEntry::GetProgIdEntries(app_info
, &entries
);
2444 // Associate each extension that the app can handle with the class. Set this
2445 // app as the default handler if and only if there is no existing default.
2446 for (std::set
<base::string16
>::const_iterator it
= file_extensions
.begin();
2447 it
!= file_extensions
.end();
2449 // Do not allow empty file extensions, or extensions beginning with a '.'.
2450 DCHECK(!it
->empty());
2451 DCHECK_NE(L
'.', (*it
)[0]);
2452 base::string16
ext(1, L
'.');
2454 RegistryEntry::GetAppExtRegistrationEntries(prog_id
, ext
, &entries
);
2456 // Regstering as the default will have no effect on Windows 8 (see
2457 // documentation for GetAppDefaultRegistrationEntries). However, if our app
2458 // is the only handler, it will automatically become the default, so the
2459 // same effect is achieved.
2460 RegistryEntry::GetAppDefaultRegistrationEntries(
2461 prog_id
, ext
, false, &entries
);
2464 return AddRegistryEntries(HKEY_CURRENT_USER
, entries
);
2468 bool ShellUtil::DeleteFileAssociations(const base::string16
& prog_id
) {
2469 // Delete the key HKEY_CURRENT_USER\Software\Classes\PROGID.
2470 base::string16
key_path(ShellUtil::kRegClasses
);
2471 key_path
.push_back(base::FilePath::kSeparators
[0]);
2472 key_path
.append(prog_id
);
2473 return InstallUtil::DeleteRegistryKey(
2474 HKEY_CURRENT_USER
, key_path
, WorkItem::kWow64Default
);
2476 // TODO(mgiuca): Remove the extension association entries. This requires that
2477 // the extensions associated with a particular prog_id are stored in that