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/file_util.h"
21 #include "base/files/file_enumerator.h"
22 #include "base/files/file_path.h"
23 #include "base/lazy_instance.h"
24 #include "base/logging.h"
26 #include "base/memory/scoped_ptr.h"
27 #include "base/memory/scoped_vector.h"
28 #include "base/path_service.h"
29 #include "base/strings/string16.h"
30 #include "base/strings/string_number_conversions.h"
31 #include "base/strings/string_split.h"
32 #include "base/strings/string_util.h"
33 #include "base/strings/utf_string_conversions.h"
34 #include "base/values.h"
35 #include "base/win/registry.h"
36 #include "base/win/scoped_co_mem.h"
37 #include "base/win/scoped_comptr.h"
38 #include "base/win/shortcut.h"
39 #include "base/win/win_util.h"
40 #include "base/win/windows_version.h"
41 #include "chrome/common/chrome_constants.h"
42 #include "chrome/common/chrome_switches.h"
43 #include "chrome/installer/util/browser_distribution.h"
44 #include "chrome/installer/util/install_util.h"
45 #include "chrome/installer/util/l10n_string_util.h"
46 #include "chrome/installer/util/master_preferences.h"
47 #include "chrome/installer/util/master_preferences_constants.h"
48 #include "chrome/installer/util/util_constants.h"
50 #include "installer_util_strings.h" // NOLINT
52 using base::win::RegKey
;
56 // An enum used to tell QuickIsChromeRegistered() which level of registration
57 // the caller wants to confirm.
58 enum RegistrationConfirmationLevel
{
59 // Only look for Chrome's ProgIds.
60 // This is sufficient when we are trying to determine the suffix of the
61 // currently running Chrome as shell integration registrations might not be
63 CONFIRM_PROGID_REGISTRATION
= 0,
64 // Confirm that Chrome is fully integrated with Windows (i.e. registered with
65 // Defaut Programs). These registrations can be in HKCU as of Windows 8.
66 // Note: Shell registration implies ProgId registration.
67 CONFIRM_SHELL_REGISTRATION
,
68 // Same as CONFIRM_SHELL_REGISTRATION, but only look in HKLM (used when
69 // uninstalling to know whether elevation is required to clean up the
71 CONFIRM_SHELL_REGISTRATION_IN_HKLM
,
74 const wchar_t kReinstallCommand
[] = L
"ReinstallCommand";
76 // Returns true if Chrome Metro is supported on this OS (Win 8 8370 or greater).
77 // TODO(gab): Change this to a simple check for Win 8 once old Win8 builds
79 bool IsChromeMetroSupported() {
80 OSVERSIONINFOEX min_version_info
= {};
81 min_version_info
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOEX
);
82 min_version_info
.dwMajorVersion
= 6;
83 min_version_info
.dwMinorVersion
= 2;
84 min_version_info
.dwBuildNumber
= 8370;
85 min_version_info
.wServicePackMajor
= 0;
86 min_version_info
.wServicePackMinor
= 0;
88 DWORDLONG condition_mask
= 0;
89 VER_SET_CONDITION(condition_mask
, VER_MAJORVERSION
, VER_GREATER_EQUAL
);
90 VER_SET_CONDITION(condition_mask
, VER_MINORVERSION
, VER_GREATER_EQUAL
);
91 VER_SET_CONDITION(condition_mask
, VER_BUILDNUMBER
, VER_GREATER_EQUAL
);
92 VER_SET_CONDITION(condition_mask
, VER_SERVICEPACKMAJOR
, VER_GREATER_EQUAL
);
93 VER_SET_CONDITION(condition_mask
, VER_SERVICEPACKMINOR
, VER_GREATER_EQUAL
);
95 DWORD type_mask
= VER_MAJORVERSION
| VER_MINORVERSION
| VER_BUILDNUMBER
|
96 VER_SERVICEPACKMAJOR
| VER_SERVICEPACKMINOR
;
98 return VerifyVersionInfo(&min_version_info
, type_mask
, condition_mask
) != 0;
101 // Returns the current (or installed) browser's ProgId (e.g.
102 // "ChromeHTML|suffix|").
103 // |suffix| can be the empty string.
104 string16
GetBrowserProgId(const string16
& suffix
) {
105 string16
chrome_html(ShellUtil::kChromeHTMLProgId
);
106 chrome_html
.append(suffix
);
108 // ProgIds cannot be longer than 39 characters.
109 // Ref: http://msdn.microsoft.com/en-us/library/aa911706.aspx.
110 // Make all new registrations comply with this requirement (existing
111 // registrations must be preserved).
112 string16 new_style_suffix
;
113 if (ShellUtil::GetUserSpecificRegistrySuffix(&new_style_suffix
) &&
114 suffix
== new_style_suffix
&& chrome_html
.length() > 39) {
116 chrome_html
.erase(39);
121 // This class is used to initialize and cache a base 32 encoding of the md5 hash
122 // of this user's sid preceded by a dot.
123 // This is guaranteed to be unique on the machine and 27 characters long
124 // (including the '.').
125 // This is then meant to be used as a suffix on all registrations that may
126 // conflict with another user-level Chrome install.
127 class UserSpecificRegistrySuffix
{
129 // All the initialization is done in the constructor to be able to build the
130 // suffix in a thread-safe manner when used in conjunction with a
132 UserSpecificRegistrySuffix();
134 // Sets |suffix| to the pre-computed suffix cached in this object.
135 // Returns true unless the initialization originally failed.
136 bool GetSuffix(string16
* suffix
);
141 DISALLOW_COPY_AND_ASSIGN(UserSpecificRegistrySuffix
);
142 }; // class UserSpecificRegistrySuffix
144 UserSpecificRegistrySuffix::UserSpecificRegistrySuffix() {
146 if (!base::win::GetUserSidString(&user_sid
)) {
150 COMPILE_ASSERT(sizeof(base::MD5Digest
) == 16, size_of_MD5_not_as_expected_
);
151 base::MD5Digest md5_digest
;
152 std::string
user_sid_ascii(UTF16ToASCII(user_sid
));
153 base::MD5Sum(user_sid_ascii
.c_str(), user_sid_ascii
.length(), &md5_digest
);
154 const string16
base32_md5(
155 ShellUtil::ByteArrayToBase32(md5_digest
.a
, arraysize(md5_digest
.a
)));
156 // The value returned by the base32 algorithm above must never change and
157 // must always be 26 characters long (i.e. if someone ever moves this to
158 // base and implements the full base32 algorithm (i.e. with appended '='
159 // signs in the output), they must provide a flag to allow this method to
160 // still request the output with no appended '=' signs).
161 DCHECK_EQ(base32_md5
.length(), 26U);
162 suffix_
.reserve(base32_md5
.length() + 1);
163 suffix_
.assign(1, L
'.');
164 suffix_
.append(base32_md5
);
167 bool UserSpecificRegistrySuffix::GetSuffix(string16
* suffix
) {
168 if (suffix_
.empty()) {
172 suffix
->assign(suffix_
);
176 // This class represents a single registry entry. The objective is to
177 // encapsulate all the registry entries required for registering Chrome at one
178 // place. This class can not be instantiated outside the class and the objects
179 // of this class type can be obtained only by calling a static method of this
181 class RegistryEntry
{
183 // A bit-field enum of places to look for this key in the Windows registry.
185 LOOK_IN_HKCU
= 1 << 0,
186 LOOK_IN_HKLM
= 1 << 1,
187 LOOK_IN_HKCU_THEN_HKLM
= LOOK_IN_HKCU
| LOOK_IN_HKLM
,
190 // Returns the Windows browser client registration key for Chrome. For
191 // example: "Software\Clients\StartMenuInternet\Chromium[.user]". Strictly
192 // speaking, we should use the name of the executable (e.g., "chrome.exe"),
193 // but that ship has sailed. The cost of switching now is re-prompting users
194 // to make Chrome their default browser, which isn't polite. |suffix| is the
195 // user-specific registration suffix; see GetUserSpecificDefaultBrowserSuffix
196 // in shell_util.h for details.
197 static string16
GetBrowserClientKey(BrowserDistribution
* dist
,
198 const string16
& suffix
) {
199 DCHECK(suffix
.empty() || suffix
[0] == L
'.');
200 return string16(ShellUtil::kRegStartMenuInternet
)
202 .append(dist
->GetBaseAppName())
206 // Returns the Windows Default Programs capabilities key for Chrome. For
208 // "Software\Clients\StartMenuInternet\Chromium[.user]\Capabilities".
209 static string16
GetCapabilitiesKey(BrowserDistribution
* dist
,
210 const string16
& suffix
) {
211 return GetBrowserClientKey(dist
, suffix
).append(L
"\\Capabilities");
214 // This method returns a list of all the registry entries that
215 // are needed to register this installation's ProgId and AppId.
216 // These entries need to be registered in HKLM prior to Win8.
217 static void GetProgIdEntries(BrowserDistribution
* dist
,
218 const string16
& chrome_exe
,
219 const string16
& suffix
,
220 ScopedVector
<RegistryEntry
>* entries
) {
222 ShellUtil::FormatIconLocation(
224 dist
->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME
)));
225 string16
open_cmd(ShellUtil::GetChromeShellOpenCmd(chrome_exe
));
226 string16
delegate_command(ShellUtil::GetChromeDelegateCommand(chrome_exe
));
227 // For user-level installs: entries for the app id and DelegateExecute verb
228 // handler will be in HKCU; thus we do not need a suffix on those entries.
230 ShellUtil::GetBrowserModelId(
231 dist
, InstallUtil::IsPerUserInstall(chrome_exe
.c_str())));
232 string16 delegate_guid
;
233 bool set_delegate_execute
=
234 IsChromeMetroSupported() &&
235 dist
->GetCommandExecuteImplClsid(&delegate_guid
);
237 // DelegateExecute ProgId. Needed for Chrome Metro in Windows 8.
238 if (set_delegate_execute
) {
239 string16
model_id_shell(ShellUtil::kRegClasses
);
240 model_id_shell
.push_back(base::FilePath::kSeparators
[0]);
241 model_id_shell
.append(app_id
);
242 model_id_shell
.append(ShellUtil::kRegExePath
);
243 model_id_shell
.append(ShellUtil::kRegShellPath
);
245 // <root hkey>\Software\Classes\<app_id>\.exe\shell @=open
246 entries
->push_back(new RegistryEntry(model_id_shell
,
247 ShellUtil::kRegVerbOpen
));
249 // Each of Chrome's shortcuts has an appid; which, as of Windows 8, is
250 // registered to handle some verbs. This registration has the side-effect
251 // that these verbs now show up in the shortcut's context menu. We
252 // mitigate this side-effect by making the context menu entries
253 // user readable/localized strings. See relevant MSDN article:
254 // http://msdn.microsoft.com/en-US/library/windows/desktop/cc144171.aspx
259 { ShellUtil::kRegVerbOpen
, -1 },
260 { ShellUtil::kRegVerbOpenNewWindow
, IDS_SHORTCUT_NEW_WINDOW_BASE
},
262 for (size_t i
= 0; i
< arraysize(verbs
); ++i
) {
263 string16
sub_path(model_id_shell
);
264 sub_path
.push_back(base::FilePath::kSeparators
[0]);
265 sub_path
.append(verbs
[i
].verb
);
267 // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb>
268 if (verbs
[i
].name_id
!= -1) {
269 // TODO(grt): http://crbug.com/75152 Write a reference to a localized
271 string16
verb_name(installer::GetLocalizedString(verbs
[i
].name_id
));
272 entries
->push_back(new RegistryEntry(sub_path
, verb_name
.c_str()));
274 entries
->push_back(new RegistryEntry(
275 sub_path
, L
"CommandId", L
"Browser.Launch"));
277 sub_path
.push_back(base::FilePath::kSeparators
[0]);
278 sub_path
.append(ShellUtil::kRegCommand
);
280 // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb>\command
281 entries
->push_back(new RegistryEntry(sub_path
, delegate_command
));
282 entries
->push_back(new RegistryEntry(
283 sub_path
, ShellUtil::kRegDelegateExecute
, delegate_guid
));
287 // File association ProgId
288 string16
chrome_html_prog_id(ShellUtil::kRegClasses
);
289 chrome_html_prog_id
.push_back(base::FilePath::kSeparators
[0]);
290 chrome_html_prog_id
.append(GetBrowserProgId(suffix
));
291 entries
->push_back(new RegistryEntry(
292 chrome_html_prog_id
, ShellUtil::kChromeHTMLProgIdDesc
));
293 entries
->push_back(new RegistryEntry(
294 chrome_html_prog_id
, ShellUtil::kRegUrlProtocol
, L
""));
295 entries
->push_back(new RegistryEntry(
296 chrome_html_prog_id
+ ShellUtil::kRegDefaultIcon
, icon_path
));
297 entries
->push_back(new RegistryEntry(
298 chrome_html_prog_id
+ ShellUtil::kRegShellOpen
, open_cmd
));
299 if (set_delegate_execute
) {
300 entries
->push_back(new RegistryEntry(
301 chrome_html_prog_id
+ ShellUtil::kRegShellOpen
,
302 ShellUtil::kRegDelegateExecute
, delegate_guid
));
305 // The following entries are required as of Windows 8, but do not
306 // depend on the DelegateExecute verb handler being set.
307 if (base::win::GetVersion() >= base::win::VERSION_WIN8
) {
308 entries
->push_back(new RegistryEntry(
309 chrome_html_prog_id
, ShellUtil::kRegAppUserModelId
, app_id
));
311 // Add \Software\Classes\ChromeHTML\Application entries
312 string16
chrome_application(chrome_html_prog_id
+
313 ShellUtil::kRegApplication
);
314 entries
->push_back(new RegistryEntry(
315 chrome_application
, ShellUtil::kRegAppUserModelId
, app_id
));
316 entries
->push_back(new RegistryEntry(
317 chrome_application
, ShellUtil::kRegApplicationIcon
, icon_path
));
318 // TODO(grt): http://crbug.com/75152 Write a reference to a localized
319 // resource for name, description, and company.
320 entries
->push_back(new RegistryEntry(
321 chrome_application
, ShellUtil::kRegApplicationName
,
322 dist
->GetDisplayName()));
323 entries
->push_back(new RegistryEntry(
324 chrome_application
, ShellUtil::kRegApplicationDescription
,
325 dist
->GetAppDescription()));
326 entries
->push_back(new RegistryEntry(
327 chrome_application
, ShellUtil::kRegApplicationCompany
,
328 dist
->GetPublisherName()));
332 // This method returns a list of the registry entries needed to declare a
333 // capability of handling a protocol on Windows.
334 static void GetProtocolCapabilityEntries(
335 BrowserDistribution
* dist
,
336 const string16
& suffix
,
337 const string16
& protocol
,
338 ScopedVector
<RegistryEntry
>* entries
) {
339 entries
->push_back(new RegistryEntry(
340 GetCapabilitiesKey(dist
, suffix
).append(L
"\\URLAssociations"),
341 protocol
, GetBrowserProgId(suffix
)));
344 // This method returns a list of the registry entries required to register
345 // this installation in "RegisteredApplications" on Windows (to appear in
346 // Default Programs, StartMenuInternet, etc.).
347 // These entries need to be registered in HKLM prior to Win8.
348 // If |suffix| is not empty, these entries are guaranteed to be unique on this
350 static void GetShellIntegrationEntries(BrowserDistribution
* dist
,
351 const string16
& chrome_exe
,
352 const string16
& suffix
,
353 ScopedVector
<RegistryEntry
>* entries
) {
354 const string16
icon_path(
355 ShellUtil::FormatIconLocation(
357 dist
->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME
)));
358 const string16
quoted_exe_path(L
"\"" + chrome_exe
+ L
"\"");
360 // Register for the Start Menu "Internet" link (pre-Win7).
361 const string16
start_menu_entry(GetBrowserClientKey(dist
, suffix
));
362 // Register Chrome's display name.
363 // TODO(grt): http://crbug.com/75152 Also set LocalizedString; see
364 // http://msdn.microsoft.com/en-us/library/windows/desktop/cc144109(v=VS.85).aspx#registering_the_display_name
365 entries
->push_back(new RegistryEntry(
366 start_menu_entry
, dist
->GetDisplayName()));
367 // Register the "open" verb for launching Chrome via the "Internet" link.
368 entries
->push_back(new RegistryEntry(
369 start_menu_entry
+ ShellUtil::kRegShellOpen
, quoted_exe_path
));
370 // Register Chrome's icon for the Start Menu "Internet" link.
371 entries
->push_back(new RegistryEntry(
372 start_menu_entry
+ ShellUtil::kRegDefaultIcon
, icon_path
));
374 // Register installation information.
375 string16
install_info(start_menu_entry
+ L
"\\InstallInfo");
376 // Note: not using CommandLine since it has ambiguous rules for quoting
378 entries
->push_back(new RegistryEntry(install_info
, kReinstallCommand
,
379 quoted_exe_path
+ L
" --" + ASCIIToWide(switches::kMakeDefaultBrowser
)));
380 entries
->push_back(new RegistryEntry(install_info
, L
"HideIconsCommand",
381 quoted_exe_path
+ L
" --" + ASCIIToWide(switches::kHideIcons
)));
382 entries
->push_back(new RegistryEntry(install_info
, L
"ShowIconsCommand",
383 quoted_exe_path
+ L
" --" + ASCIIToWide(switches::kShowIcons
)));
384 entries
->push_back(new RegistryEntry(install_info
, L
"IconsVisible", 1));
386 // Register with Default Programs.
387 const string16
reg_app_name(dist
->GetBaseAppName().append(suffix
));
388 // Tell Windows where to find Chrome's Default Programs info.
389 const string16
capabilities(GetCapabilitiesKey(dist
, suffix
));
390 entries
->push_back(new RegistryEntry(ShellUtil::kRegRegisteredApplications
,
391 reg_app_name
, capabilities
));
392 // Write out Chrome's Default Programs info.
393 // TODO(grt): http://crbug.com/75152 Write a reference to a localized
394 // resource rather than this.
395 entries
->push_back(new RegistryEntry(
396 capabilities
, ShellUtil::kRegApplicationDescription
,
397 dist
->GetLongAppDescription()));
398 entries
->push_back(new RegistryEntry(
399 capabilities
, ShellUtil::kRegApplicationIcon
, icon_path
));
400 entries
->push_back(new RegistryEntry(
401 capabilities
, ShellUtil::kRegApplicationName
,
402 dist
->GetDisplayName()));
404 entries
->push_back(new RegistryEntry(capabilities
+ L
"\\Startmenu",
405 L
"StartMenuInternet", reg_app_name
));
407 const string16
html_prog_id(GetBrowserProgId(suffix
));
408 for (int i
= 0; ShellUtil::kPotentialFileAssociations
[i
] != NULL
; i
++) {
409 entries
->push_back(new RegistryEntry(
410 capabilities
+ L
"\\FileAssociations",
411 ShellUtil::kPotentialFileAssociations
[i
], html_prog_id
));
413 for (int i
= 0; ShellUtil::kPotentialProtocolAssociations
[i
] != NULL
;
415 entries
->push_back(new RegistryEntry(
416 capabilities
+ L
"\\URLAssociations",
417 ShellUtil::kPotentialProtocolAssociations
[i
], html_prog_id
));
421 // This method returns a list of the registry entries required for this
422 // installation to be registered in the Windows shell.
425 // http://msdn.microsoft.com/en-us/library/windows/desktop/ee872121
426 // - File Associations
427 // http://msdn.microsoft.com/en-us/library/bb166549
428 // These entries need to be registered in HKLM prior to Win8.
429 static void GetAppRegistrationEntries(const string16
& chrome_exe
,
430 const string16
& suffix
,
431 ScopedVector
<RegistryEntry
>* entries
) {
432 const base::FilePath
chrome_path(chrome_exe
);
433 string16
app_path_key(ShellUtil::kAppPathsRegistryKey
);
434 app_path_key
.push_back(base::FilePath::kSeparators
[0]);
435 app_path_key
.append(chrome_path
.BaseName().value());
436 entries
->push_back(new RegistryEntry(app_path_key
, chrome_exe
));
437 entries
->push_back(new RegistryEntry(app_path_key
,
438 ShellUtil::kAppPathsRegistryPathName
, chrome_path
.DirName().value()));
440 const string16
html_prog_id(GetBrowserProgId(suffix
));
441 for (int i
= 0; ShellUtil::kPotentialFileAssociations
[i
] != NULL
; i
++) {
442 string16
key(ShellUtil::kRegClasses
);
443 key
.push_back(base::FilePath::kSeparators
[0]);
444 key
.append(ShellUtil::kPotentialFileAssociations
[i
]);
445 key
.push_back(base::FilePath::kSeparators
[0]);
446 key
.append(ShellUtil::kRegOpenWithProgids
);
447 entries
->push_back(new RegistryEntry(key
, html_prog_id
, string16()));
451 // This method returns a list of all the user level registry entries that
452 // are needed to make Chromium the default handler for a protocol on XP.
453 static void GetXPStyleUserProtocolEntries(
454 const string16
& protocol
,
455 const string16
& chrome_icon
,
456 const string16
& chrome_open
,
457 ScopedVector
<RegistryEntry
>* entries
) {
458 // Protocols associations.
459 string16
url_key(ShellUtil::kRegClasses
);
460 url_key
.push_back(base::FilePath::kSeparators
[0]);
461 url_key
.append(protocol
);
463 // This registry value tells Windows that this 'class' is a URL scheme
464 // so IE, explorer and other apps will route it to our handler.
465 // <root hkey>\Software\Classes\<protocol>\URL Protocol
466 entries
->push_back(new RegistryEntry(url_key
,
467 ShellUtil::kRegUrlProtocol
, L
""));
469 // <root hkey>\Software\Classes\<protocol>\DefaultIcon
470 string16 icon_key
= url_key
+ ShellUtil::kRegDefaultIcon
;
471 entries
->push_back(new RegistryEntry(icon_key
, chrome_icon
));
473 // <root hkey>\Software\Classes\<protocol>\shell\open\command
474 string16 shell_key
= url_key
+ ShellUtil::kRegShellOpen
;
475 entries
->push_back(new RegistryEntry(shell_key
, chrome_open
));
477 // <root hkey>\Software\Classes\<protocol>\shell\open\ddeexec
478 string16 dde_key
= url_key
+ L
"\\shell\\open\\ddeexec";
479 entries
->push_back(new RegistryEntry(dde_key
, L
""));
481 // <root hkey>\Software\Classes\<protocol>\shell\@
482 string16 protocol_shell_key
= url_key
+ ShellUtil::kRegShellPath
;
483 entries
->push_back(new RegistryEntry(protocol_shell_key
, L
"open"));
486 // This method returns a list of all the user level registry entries that
487 // are needed to make Chromium default browser on XP.
488 // Some of these entries are irrelevant in recent versions of Windows, but
489 // we register them anyways as some legacy apps are hardcoded to lookup those
491 static void GetXPStyleDefaultBrowserUserEntries(
492 BrowserDistribution
* dist
,
493 const string16
& chrome_exe
,
494 const string16
& suffix
,
495 ScopedVector
<RegistryEntry
>* entries
) {
496 // File extension associations.
497 string16
html_prog_id(GetBrowserProgId(suffix
));
498 for (int i
= 0; ShellUtil::kDefaultFileAssociations
[i
] != NULL
; i
++) {
499 string16
ext_key(ShellUtil::kRegClasses
);
500 ext_key
.push_back(base::FilePath::kSeparators
[0]);
501 ext_key
.append(ShellUtil::kDefaultFileAssociations
[i
]);
502 entries
->push_back(new RegistryEntry(ext_key
, html_prog_id
));
505 // Protocols associations.
506 string16 chrome_open
= ShellUtil::GetChromeShellOpenCmd(chrome_exe
);
507 string16 chrome_icon
=
508 ShellUtil::FormatIconLocation(
510 dist
->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME
));
511 for (int i
= 0; ShellUtil::kBrowserProtocolAssociations
[i
] != NULL
; i
++) {
512 GetXPStyleUserProtocolEntries(ShellUtil::kBrowserProtocolAssociations
[i
],
513 chrome_icon
, chrome_open
, entries
);
516 // start->Internet shortcut.
517 string16
start_menu(ShellUtil::kRegStartMenuInternet
);
518 string16 app_name
= dist
->GetBaseAppName() + suffix
;
519 entries
->push_back(new RegistryEntry(start_menu
, app_name
));
522 // Generate work_item tasks required to create current registry entry and
523 // add them to the given work item list.
524 void AddToWorkItemList(HKEY root
, WorkItemList
*items
) const {
525 items
->AddCreateRegKeyWorkItem(root
, key_path_
);
527 items
->AddSetRegValueWorkItem(root
, key_path_
, name_
, value_
, true);
529 items
->AddSetRegValueWorkItem(root
, key_path_
, name_
, int_value_
, true);
533 // Checks if the current registry entry exists in HKCU\|key_path_|\|name_|
534 // and value is |value_|. If the key does NOT exist in HKCU, checks for
535 // the correct name and value in HKLM.
536 // |look_for_in| specifies roots (HKCU and/or HKLM) in which to look for the
537 // key, unspecified roots are not looked into (i.e. the the key is assumed not
538 // to exist in them).
539 // |look_for_in| must at least specify one root to look into.
540 // If |look_for_in| is LOOK_IN_HKCU_THEN_HKLM, this method mimics Windows'
541 // behavior when searching in HKCR (HKCU takes precedence over HKLM). For
542 // registrations outside of HKCR on versions of Windows prior to Win8,
543 // Chrome's values go in HKLM. This function will make unnecessary (but
544 // harmless) queries into HKCU in that case.
545 bool ExistsInRegistry(uint32 look_for_in
) const {
548 RegistryStatus status
= DOES_NOT_EXIST
;
549 if (look_for_in
& LOOK_IN_HKCU
)
550 status
= StatusInRegistryUnderRoot(HKEY_CURRENT_USER
);
551 if (status
== DOES_NOT_EXIST
&& (look_for_in
& LOOK_IN_HKLM
))
552 status
= StatusInRegistryUnderRoot(HKEY_LOCAL_MACHINE
);
553 return status
== SAME_VALUE
;
557 // States this RegistryKey can be in compared to the registry.
558 enum RegistryStatus
{
559 // |name_| does not exist in the registry
561 // |name_| exists, but its value != |value_|
563 // |name_| exists and its value is |value_|
567 // Create a object that represent default value of a key
568 RegistryEntry(const string16
& key_path
, const string16
& value
)
569 : key_path_(key_path
), name_(),
570 is_string_(true), value_(value
), int_value_(0) {
573 // Create a object that represent a key of type REG_SZ
574 RegistryEntry(const string16
& key_path
, const string16
& name
,
575 const string16
& value
)
576 : key_path_(key_path
), name_(name
),
577 is_string_(true), value_(value
), int_value_(0) {
580 // Create a object that represent a key of integer type
581 RegistryEntry(const string16
& key_path
, const string16
& name
,
583 : key_path_(key_path
), name_(name
),
584 is_string_(false), value_(), int_value_(value
) {
587 string16 key_path_
; // key path for the registry entry
588 string16 name_
; // name of the registry entry
589 bool is_string_
; // true if current registry entry is of type REG_SZ
590 string16 value_
; // string value (useful if is_string_ = true)
591 DWORD int_value_
; // integer value (useful if is_string_ = false)
593 // Helper function for ExistsInRegistry().
594 // Returns the RegistryStatus of the current registry entry in
595 // |root|\|key_path_|\|name_|.
596 RegistryStatus
StatusInRegistryUnderRoot(HKEY root
) const {
597 RegKey
key(root
, key_path_
.c_str(), KEY_QUERY_VALUE
);
599 bool correct_value
= false;
602 found
= key
.ReadValue(name_
.c_str(), &read_value
) == ERROR_SUCCESS
;
603 correct_value
= read_value
.size() == value_
.size() &&
604 std::equal(value_
.begin(), value_
.end(), read_value
.begin(),
605 base::CaseInsensitiveCompare
<wchar_t>());
608 found
= key
.ReadValueDW(name_
.c_str(), &read_value
) == ERROR_SUCCESS
;
609 correct_value
= read_value
== int_value_
;
612 (correct_value
? SAME_VALUE
: DIFFERENT_VALUE
) : DOES_NOT_EXIST
;
615 DISALLOW_COPY_AND_ASSIGN(RegistryEntry
);
616 }; // class RegistryEntry
619 // This method converts all the RegistryEntries from the given list to
620 // Set/CreateRegWorkItems and runs them using WorkItemList.
621 bool AddRegistryEntries(HKEY root
, const ScopedVector
<RegistryEntry
>& entries
) {
622 scoped_ptr
<WorkItemList
> items(WorkItem::CreateWorkItemList());
624 for (ScopedVector
<RegistryEntry
>::const_iterator itr
= entries
.begin();
625 itr
!= entries
.end(); ++itr
)
626 (*itr
)->AddToWorkItemList(root
, items
.get());
628 // Apply all the registry changes and if there is a problem, rollback
636 // Checks that all |entries| are present on this computer.
637 // |look_for_in| is passed to RegistryEntry::ExistsInRegistry(). Documentation
638 // for it can be found there.
639 bool AreEntriesRegistered(const ScopedVector
<RegistryEntry
>& entries
,
640 uint32 look_for_in
) {
641 bool registered
= true;
642 for (ScopedVector
<RegistryEntry
>::const_iterator itr
= entries
.begin();
643 registered
&& itr
!= entries
.end(); ++itr
) {
644 // We do not need registered = registered && ... since the loop condition
645 // is set to exit early.
646 registered
= (*itr
)->ExistsInRegistry(look_for_in
);
651 // Checks that all required registry entries for Chrome are already present
652 // on this computer. See RegistryEntry::ExistsInRegistry for the behavior of
654 // Note: between r133333 and r154145 we were registering parts of Chrome in HKCU
655 // and parts in HKLM for user-level installs; we now always register everything
656 // under a single registry root. Not doing so caused http://crbug.com/144910 for
657 // users who first-installed Chrome in that revision range (those users are
658 // still impacted by http://crbug.com/144910). This method will keep returning
659 // true for affected users (i.e. who have all the registrations, but over both
661 bool IsChromeRegistered(BrowserDistribution
* dist
,
662 const string16
& chrome_exe
,
663 const string16
& suffix
,
664 uint32 look_for_in
) {
665 ScopedVector
<RegistryEntry
> entries
;
666 RegistryEntry::GetProgIdEntries(dist
, chrome_exe
, suffix
, &entries
);
667 RegistryEntry::GetShellIntegrationEntries(dist
, chrome_exe
, suffix
, &entries
);
668 RegistryEntry::GetAppRegistrationEntries(chrome_exe
, suffix
, &entries
);
669 return AreEntriesRegistered(entries
, look_for_in
);
672 // This method checks if Chrome is already registered on the local machine
673 // for the requested protocol. It just checks the one value required for this.
674 // See RegistryEntry::ExistsInRegistry for the behavior of |look_for_in|.
675 bool IsChromeRegisteredForProtocol(BrowserDistribution
* dist
,
676 const string16
& suffix
,
677 const string16
& protocol
,
678 uint32 look_for_in
) {
679 ScopedVector
<RegistryEntry
> entries
;
680 RegistryEntry::GetProtocolCapabilityEntries(dist
, suffix
, protocol
, &entries
);
681 return AreEntriesRegistered(entries
, look_for_in
);
684 // This method registers Chrome on Vista by launching an elevated setup.exe.
685 // That will show the user the standard Vista elevation prompt. If the user
686 // accepts it the new process will make the necessary changes and return SUCCESS
687 // that we capture and return.
688 // If protocol is non-empty we will also register Chrome as being capable of
689 // handling the protocol.
690 bool ElevateAndRegisterChrome(BrowserDistribution
* dist
,
691 const string16
& chrome_exe
,
692 const string16
& suffix
,
693 const string16
& protocol
) {
694 // Only user-level installs prior to Windows 8 should need to elevate to
696 DCHECK(InstallUtil::IsPerUserInstall(chrome_exe
.c_str()));
697 DCHECK_LT(base::win::GetVersion(), base::win::VERSION_WIN8
);
698 base::FilePath exe_path
=
699 base::FilePath::FromWStringHack(chrome_exe
).DirName()
700 .Append(installer::kSetupExe
);
701 if (!base::PathExists(exe_path
)) {
702 HKEY reg_root
= InstallUtil::IsPerUserInstall(chrome_exe
.c_str()) ?
703 HKEY_CURRENT_USER
: HKEY_LOCAL_MACHINE
;
704 RegKey
key(reg_root
, dist
->GetUninstallRegPath().c_str(), KEY_READ
);
705 string16 uninstall_string
;
706 key
.ReadValue(installer::kUninstallStringField
, &uninstall_string
);
707 CommandLine command_line
= CommandLine::FromString(uninstall_string
);
708 exe_path
= command_line
.GetProgram();
711 if (base::PathExists(exe_path
)) {
712 CommandLine
cmd(exe_path
);
713 cmd
.AppendSwitchNative(installer::switches::kRegisterChromeBrowser
,
715 if (!suffix
.empty()) {
716 cmd
.AppendSwitchNative(
717 installer::switches::kRegisterChromeBrowserSuffix
, suffix
);
720 CommandLine
& browser_command_line
= *CommandLine::ForCurrentProcess();
721 if (browser_command_line
.HasSwitch(switches::kChromeFrame
)) {
722 cmd
.AppendSwitch(installer::switches::kChromeFrame
);
725 if (!protocol
.empty()) {
726 cmd
.AppendSwitchNative(
727 installer::switches::kRegisterURLProtocol
, protocol
);
731 InstallUtil::ExecuteExeAsAdmin(cmd
, &ret_val
);
738 // Launches the Windows 7 and Windows 8 dialog for picking the application to
739 // handle the given protocol. Most importantly, this is used to set the default
740 // handler for http (and, implicitly with it, https). In that case it is also
741 // known as the 'how do you want to open webpages' dialog.
742 // It is required that Chrome be already *registered* for the given protocol.
743 bool LaunchSelectDefaultProtocolHandlerDialog(const wchar_t* protocol
) {
745 OPENASINFO open_as_info
= {};
746 open_as_info
.pcszFile
= protocol
;
747 open_as_info
.oaifInFlags
=
748 OAIF_URL_PROTOCOL
| OAIF_FORCE_REGISTRATION
| OAIF_REGISTER_EXT
;
749 HRESULT hr
= SHOpenWithDialog(NULL
, &open_as_info
);
750 DLOG_IF(WARNING
, FAILED(hr
)) << "Failed to set as default " << protocol
751 << " handler; hr=0x" << std::hex
<< hr
;
754 SHChangeNotify(SHCNE_ASSOCCHANGED
, SHCNF_IDLIST
, NULL
, NULL
);
758 // Launches the Windows 7 and Windows 8 application association dialog, which
759 // is the only documented way to make a browser the default browser on
761 bool LaunchApplicationAssociationDialog(const string16
& app_id
) {
762 base::win::ScopedComPtr
<IApplicationAssociationRegistrationUI
> aarui
;
763 HRESULT hr
= aarui
.CreateInstance(CLSID_ApplicationAssociationRegistrationUI
);
766 hr
= aarui
->LaunchAdvancedAssociationUI(app_id
.c_str());
767 return SUCCEEDED(hr
);
770 // Returns true if the current install's |chrome_exe| has been registered with
772 // |confirmation_level| is the level of verification desired as described in
773 // the RegistrationConfirmationLevel enum above.
774 // |suffix| can be the empty string (this is used to support old installs
775 // where we used to not suffix user-level installs if they were the first to
776 // request the non-suffixed registry entries on the machine).
777 // NOTE: This a quick check that only validates that a single registry entry
778 // points to |chrome_exe|. This should only be used at run-time to determine
779 // how Chrome is registered, not to know whether the registration is complete
780 // at install-time (IsChromeRegistered() can be used for that).
781 bool QuickIsChromeRegistered(BrowserDistribution
* dist
,
782 const string16
& chrome_exe
,
783 const string16
& suffix
,
784 RegistrationConfirmationLevel confirmation_level
) {
785 // Get the appropriate key to look for based on the level desired.
787 switch (confirmation_level
) {
788 case CONFIRM_PROGID_REGISTRATION
:
789 // Software\Classes\ChromeHTML|suffix|
790 reg_key
= ShellUtil::kRegClasses
;
791 reg_key
.push_back(base::FilePath::kSeparators
[0]);
792 reg_key
.append(ShellUtil::kChromeHTMLProgId
);
793 reg_key
.append(suffix
);
795 case CONFIRM_SHELL_REGISTRATION
:
796 case CONFIRM_SHELL_REGISTRATION_IN_HKLM
:
797 // Software\Clients\StartMenuInternet\Google Chrome|suffix|
798 reg_key
= RegistryEntry::GetBrowserClientKey(dist
, suffix
);
804 reg_key
.append(ShellUtil::kRegShellOpen
);
806 // ProgId registrations are allowed to reside in HKCU for user-level installs
807 // (and values there have priority over values in HKLM). The same is true for
808 // shell integration entries as of Windows 8.
809 if (confirmation_level
== CONFIRM_PROGID_REGISTRATION
||
810 (confirmation_level
== CONFIRM_SHELL_REGISTRATION
&&
811 base::win::GetVersion() >= base::win::VERSION_WIN8
)) {
812 const RegKey
key_hkcu(HKEY_CURRENT_USER
, reg_key
.c_str(), KEY_QUERY_VALUE
);
814 // If |reg_key| is present in HKCU, assert that it points to |chrome_exe|.
815 // Otherwise, fall back on an HKLM lookup below.
816 if (key_hkcu
.ReadValue(L
"", &hkcu_value
) == ERROR_SUCCESS
) {
817 return InstallUtil::ProgramCompare(
818 base::FilePath(chrome_exe
)).Evaluate(hkcu_value
);
822 // Assert that |reg_key| points to |chrome_exe| in HKLM.
823 const RegKey
key_hklm(HKEY_LOCAL_MACHINE
, reg_key
.c_str(), KEY_QUERY_VALUE
);
825 if (key_hklm
.ReadValue(L
"", &hklm_value
) == ERROR_SUCCESS
) {
826 return InstallUtil::ProgramCompare(
827 base::FilePath(chrome_exe
)).Evaluate(hklm_value
);
832 // Sets |suffix| to a 27 character string that is specific to this user on this
833 // machine (on user-level installs only).
834 // To support old-style user-level installs however, |suffix| is cleared if the
835 // user currently owns the non-suffixed HKLM registrations.
836 // |suffix| can also be set to the user's username if the current install is
837 // suffixed as per the old-style registrations.
838 // |suffix| is cleared on system-level installs.
839 // |suffix| should then be appended to all Chrome properties that may conflict
840 // with other Chrome user-level installs.
841 // Returns true unless one of the underlying calls fails.
842 bool GetInstallationSpecificSuffix(BrowserDistribution
* dist
,
843 const string16
& chrome_exe
,
845 if (!InstallUtil::IsPerUserInstall(chrome_exe
.c_str()) ||
846 QuickIsChromeRegistered(dist
, chrome_exe
, string16(),
847 CONFIRM_SHELL_REGISTRATION
)) {
848 // No suffix on system-level installs and user-level installs already
849 // registered with no suffix.
854 // Get the old suffix for the check below.
855 if (!ShellUtil::GetOldUserSpecificRegistrySuffix(suffix
)) {
859 if (QuickIsChromeRegistered(dist
, chrome_exe
, *suffix
,
860 CONFIRM_SHELL_REGISTRATION
)) {
861 // Username suffix for installs that are suffixed as per the old-style.
865 return ShellUtil::GetUserSpecificRegistrySuffix(suffix
);
868 // Returns the root registry key (HKLM or HKCU) under which registrations must
869 // be placed for this install. As of Windows 8 everything can go in HKCU for
870 // per-user installs.
871 HKEY
DetermineRegistrationRoot(bool is_per_user
) {
872 return is_per_user
&& base::win::GetVersion() >= base::win::VERSION_WIN8
?
873 HKEY_CURRENT_USER
: HKEY_LOCAL_MACHINE
;
876 // Associates Chrome with supported protocols and file associations. This should
877 // not be required on Vista+ but since some applications still read
878 // Software\Classes\http key directly, we have to do this on Vista+ as well.
879 bool RegisterChromeAsDefaultXPStyle(BrowserDistribution
* dist
,
881 const string16
& chrome_exe
) {
883 ScopedVector
<RegistryEntry
> entries
;
884 RegistryEntry::GetXPStyleDefaultBrowserUserEntries(
886 ShellUtil::GetCurrentInstallationSuffix(dist
, chrome_exe
), &entries
);
888 // Change the default browser for current user.
889 if ((shell_change
& ShellUtil::CURRENT_USER
) &&
890 !AddRegistryEntries(HKEY_CURRENT_USER
, entries
)) {
892 LOG(ERROR
) << "Could not make Chrome default browser (XP/current user).";
895 // Chrome as default browser at system level.
896 if ((shell_change
& ShellUtil::SYSTEM_LEVEL
) &&
897 !AddRegistryEntries(HKEY_LOCAL_MACHINE
, entries
)) {
899 LOG(ERROR
) << "Could not make Chrome default browser (XP/system level).";
905 // Associates Chrome with |protocol| in the registry. This should not be
906 // required on Vista+ but since some applications still read these registry
907 // keys directly, we have to do this on Vista+ as well.
908 // See http://msdn.microsoft.com/library/aa767914.aspx for more details.
909 bool RegisterChromeAsDefaultProtocolClientXPStyle(BrowserDistribution
* dist
,
910 const string16
& chrome_exe
,
911 const string16
& protocol
) {
912 ScopedVector
<RegistryEntry
> entries
;
913 const string16
chrome_open(ShellUtil::GetChromeShellOpenCmd(chrome_exe
));
914 const string16
chrome_icon(
915 ShellUtil::FormatIconLocation(
917 dist
->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME
)));
918 RegistryEntry::GetXPStyleUserProtocolEntries(protocol
, chrome_icon
,
919 chrome_open
, &entries
);
920 // Change the default protocol handler for current user.
921 if (!AddRegistryEntries(HKEY_CURRENT_USER
, entries
)) {
922 LOG(ERROR
) << "Could not make Chrome default protocol client (XP).";
929 // Returns |properties.shortcut_name| if the property is set, otherwise it
930 // returns dist->GetShortcutName(BrowserDistribution::SHORTCUT_CHROME). In any
931 // case, it makes sure the return value is suffixed with ".lnk".
932 string16
ExtractShortcutNameFromProperties(
933 BrowserDistribution
* dist
,
934 const ShellUtil::ShortcutProperties
& properties
) {
936 string16 shortcut_name
;
937 if (properties
.has_shortcut_name()) {
938 shortcut_name
= properties
.shortcut_name
;
941 dist
->GetShortcutName(BrowserDistribution::SHORTCUT_CHROME
);
944 if (!EndsWith(shortcut_name
, installer::kLnkExt
, false))
945 shortcut_name
.append(installer::kLnkExt
);
947 return shortcut_name
;
950 // Converts ShellUtil::ShortcutOperation to the best-matching value in
951 // base::win::ShortcutOperation.
952 base::win::ShortcutOperation
TranslateShortcutOperation(
953 ShellUtil::ShortcutOperation operation
) {
955 case ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS
: // Falls through.
956 case ShellUtil::SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL
:
957 return base::win::SHORTCUT_CREATE_ALWAYS
;
959 case ShellUtil::SHELL_SHORTCUT_UPDATE_EXISTING
:
960 return base::win::SHORTCUT_UPDATE_EXISTING
;
962 case ShellUtil::SHELL_SHORTCUT_REPLACE_EXISTING
:
963 return base::win::SHORTCUT_REPLACE_EXISTING
;
967 return base::win::SHORTCUT_REPLACE_EXISTING
;
971 // Returns a base::win::ShortcutProperties struct containing the properties
972 // to set on the shortcut based on the provided ShellUtil::ShortcutProperties.
973 base::win::ShortcutProperties
TranslateShortcutProperties(
974 const ShellUtil::ShortcutProperties
& properties
) {
975 base::win::ShortcutProperties shortcut_properties
;
977 if (properties
.has_target()) {
978 shortcut_properties
.set_target(properties
.target
);
979 DCHECK(!properties
.target
.DirName().empty());
980 shortcut_properties
.set_working_dir(properties
.target
.DirName());
983 if (properties
.has_arguments())
984 shortcut_properties
.set_arguments(properties
.arguments
);
986 if (properties
.has_description())
987 shortcut_properties
.set_description(properties
.description
);
989 if (properties
.has_icon())
990 shortcut_properties
.set_icon(properties
.icon
, properties
.icon_index
);
992 if (properties
.has_app_id())
993 shortcut_properties
.set_app_id(properties
.app_id
);
995 if (properties
.has_dual_mode())
996 shortcut_properties
.set_dual_mode(properties
.dual_mode
);
998 return shortcut_properties
;
1001 // Cleans up an old verb (run) we used to register in
1002 // <root>\Software\Classes\Chrome<.suffix>\.exe\shell\run on Windows 8.
1003 void RemoveRunVerbOnWindows8(
1004 BrowserDistribution
* dist
,
1005 const string16
& chrome_exe
) {
1006 if (IsChromeMetroSupported()) {
1007 bool is_per_user_install
=InstallUtil::IsPerUserInstall(chrome_exe
.c_str());
1008 HKEY root_key
= DetermineRegistrationRoot(is_per_user_install
);
1009 // There's no need to rollback, so forgo the usual work item lists and just
1010 // remove the key from the registry.
1011 string16
run_verb_key(ShellUtil::kRegClasses
);
1012 run_verb_key
.push_back(base::FilePath::kSeparators
[0]);
1013 run_verb_key
.append(ShellUtil::GetBrowserModelId(
1014 dist
, is_per_user_install
));
1015 run_verb_key
.append(ShellUtil::kRegExePath
);
1016 run_verb_key
.append(ShellUtil::kRegShellPath
);
1017 run_verb_key
.push_back(base::FilePath::kSeparators
[0]);
1018 run_verb_key
.append(ShellUtil::kRegVerbRun
);
1019 InstallUtil::DeleteRegistryKey(root_key
, run_verb_key
);
1023 // Gets the short (8.3) form of |path|, putting the result in |short_path| and
1024 // returning true on success. |short_path| is not modified on failure.
1025 bool ShortNameFromPath(const base::FilePath
& path
, string16
* short_path
) {
1027 string16
result(MAX_PATH
, L
'\0');
1028 DWORD short_length
= GetShortPathName(path
.value().c_str(), &result
[0],
1030 if (short_length
== 0 || short_length
> result
.size()) {
1031 PLOG(ERROR
) << "Error getting short (8.3) path";
1035 result
.resize(short_length
);
1036 short_path
->swap(result
);
1040 // Probe using IApplicationAssociationRegistration::QueryCurrentDefault
1041 // (Windows 8); see ProbeProtocolHandlers. This mechanism is not suitable for
1042 // use on previous versions of Windows despite the presence of
1043 // QueryCurrentDefault on them since versions of Windows prior to Windows 8
1044 // did not perform validation on the ProgID registered as the current default.
1045 // As a result, stale ProgIDs could be returned, leading to false positives.
1046 ShellUtil::DefaultState
ProbeCurrentDefaultHandlers(
1047 const wchar_t* const* protocols
,
1048 size_t num_protocols
) {
1049 base::win::ScopedComPtr
<IApplicationAssociationRegistration
> registration
;
1050 HRESULT hr
= registration
.CreateInstance(
1051 CLSID_ApplicationAssociationRegistration
, NULL
, CLSCTX_INPROC
);
1053 return ShellUtil::UNKNOWN_DEFAULT
;
1055 BrowserDistribution
* dist
= BrowserDistribution::GetDistribution();
1056 base::FilePath chrome_exe
;
1057 if (!PathService::Get(base::FILE_EXE
, &chrome_exe
)) {
1059 return ShellUtil::UNKNOWN_DEFAULT
;
1061 string16
prog_id(ShellUtil::kChromeHTMLProgId
);
1062 prog_id
+= ShellUtil::GetCurrentInstallationSuffix(dist
, chrome_exe
.value());
1064 for (size_t i
= 0; i
< num_protocols
; ++i
) {
1065 base::win::ScopedCoMem
<wchar_t> current_app
;
1066 hr
= registration
->QueryCurrentDefault(protocols
[i
], AT_URLPROTOCOL
,
1067 AL_EFFECTIVE
, ¤t_app
);
1068 if (FAILED(hr
) || prog_id
.compare(current_app
) != 0)
1069 return ShellUtil::NOT_DEFAULT
;
1072 return ShellUtil::IS_DEFAULT
;
1075 // Probe using IApplicationAssociationRegistration::QueryAppIsDefault (Vista and
1076 // Windows 7); see ProbeProtocolHandlers.
1077 ShellUtil::DefaultState
ProbeAppIsDefaultHandlers(
1078 const wchar_t* const* protocols
,
1079 size_t num_protocols
) {
1080 base::win::ScopedComPtr
<IApplicationAssociationRegistration
> registration
;
1081 HRESULT hr
= registration
.CreateInstance(
1082 CLSID_ApplicationAssociationRegistration
, NULL
, CLSCTX_INPROC
);
1084 return ShellUtil::UNKNOWN_DEFAULT
;
1086 BrowserDistribution
* dist
= BrowserDistribution::GetDistribution();
1087 base::FilePath chrome_exe
;
1088 if (!PathService::Get(base::FILE_EXE
, &chrome_exe
)) {
1090 return ShellUtil::UNKNOWN_DEFAULT
;
1092 string16
app_name(ShellUtil::GetApplicationName(dist
, chrome_exe
.value()));
1095 for (size_t i
= 0; i
< num_protocols
; ++i
) {
1097 hr
= registration
->QueryAppIsDefault(protocols
[i
], AT_URLPROTOCOL
,
1098 AL_EFFECTIVE
, app_name
.c_str(), &result
);
1099 if (FAILED(hr
) || result
== FALSE
)
1100 return ShellUtil::NOT_DEFAULT
;
1103 return ShellUtil::IS_DEFAULT
;
1106 // Probe the current commands registered to handle the shell "open" verb for
1107 // |protocols| (Windows XP); see ProbeProtocolHandlers.
1108 ShellUtil::DefaultState
ProbeOpenCommandHandlers(
1109 const wchar_t* const* protocols
,
1110 size_t num_protocols
) {
1111 // Get the path to the current exe (Chrome).
1112 base::FilePath app_path
;
1113 if (!PathService::Get(base::FILE_EXE
, &app_path
)) {
1114 LOG(ERROR
) << "Error getting app exe path";
1115 return ShellUtil::UNKNOWN_DEFAULT
;
1118 // Get its short (8.3) form.
1119 string16 short_app_path
;
1120 if (!ShortNameFromPath(app_path
, &short_app_path
))
1121 return ShellUtil::UNKNOWN_DEFAULT
;
1123 const HKEY root_key
= HKEY_CLASSES_ROOT
;
1125 base::win::RegKey key
;
1127 CommandLine
command_line(CommandLine::NO_PROGRAM
);
1128 string16 short_path
;
1130 for (size_t i
= 0; i
< num_protocols
; ++i
) {
1131 // Get the command line from HKCU\<protocol>\shell\open\command.
1132 key_path
.assign(protocols
[i
]).append(ShellUtil::kRegShellOpen
);
1133 if ((key
.Open(root_key
, key_path
.c_str(),
1134 KEY_QUERY_VALUE
) != ERROR_SUCCESS
) ||
1135 (key
.ReadValue(L
"", &value
) != ERROR_SUCCESS
)) {
1136 return ShellUtil::NOT_DEFAULT
;
1139 // Need to normalize path in case it's been munged.
1140 command_line
= CommandLine::FromString(value
);
1141 if (!ShortNameFromPath(command_line
.GetProgram(), &short_path
))
1142 return ShellUtil::UNKNOWN_DEFAULT
;
1144 if (!base::FilePath::CompareEqualIgnoreCase(short_path
, short_app_path
))
1145 return ShellUtil::NOT_DEFAULT
;
1148 return ShellUtil::IS_DEFAULT
;
1151 // A helper function that probes default protocol handler registration (in a
1152 // manner appropriate for the current version of Windows) to determine if
1153 // Chrome is the default handler for |protocols|. Returns IS_DEFAULT
1154 // only if Chrome is the default for all specified protocols.
1155 ShellUtil::DefaultState
ProbeProtocolHandlers(
1156 const wchar_t* const* protocols
,
1157 size_t num_protocols
) {
1158 DCHECK(!num_protocols
|| protocols
);
1159 if (DCHECK_IS_ON()) {
1160 for (size_t i
= 0; i
< num_protocols
; ++i
)
1161 DCHECK(protocols
[i
] && *protocols
[i
]);
1164 const base::win::Version windows_version
= base::win::GetVersion();
1166 if (windows_version
>= base::win::VERSION_WIN8
)
1167 return ProbeCurrentDefaultHandlers(protocols
, num_protocols
);
1168 else if (windows_version
>= base::win::VERSION_VISTA
)
1169 return ProbeAppIsDefaultHandlers(protocols
, num_protocols
);
1171 return ProbeOpenCommandHandlers(protocols
, num_protocols
);
1174 // (Windows 8+) Finds and stores an app shortcuts folder path in *|path|.
1175 // Returns true on success.
1176 bool GetAppShortcutsFolder(BrowserDistribution
* dist
,
1177 ShellUtil::ShellChange level
,
1178 base::FilePath
*path
) {
1180 DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8
);
1182 base::FilePath folder
;
1183 if (!PathService::Get(base::DIR_APP_SHORTCUTS
, &folder
)) {
1184 LOG(ERROR
) << "Could not get application shortcuts location.";
1188 folder
= folder
.Append(
1189 ShellUtil::GetBrowserModelId(dist
, level
== ShellUtil::CURRENT_USER
));
1190 if (!base::DirectoryExists(folder
)) {
1191 VLOG(1) << "No start screen shortcuts.";
1199 // Shortcut filters for BatchShortcutAction().
1201 typedef base::Callback
<bool(const base::FilePath
& /*shortcut_path*/,
1202 const string16
& /*args*/)>
1203 ShortcutFilterCallback
;
1205 // FilterTargetEq is a shortcut filter that matches only shortcuts that have a
1206 // specific target, and optionally matches shortcuts that have non-empty
1208 class FilterTargetEq
{
1210 FilterTargetEq(const base::FilePath
& desired_target_exe
, bool require_args
);
1212 // Returns true if filter rules are satisfied, i.e.:
1213 // - |target_path|'s target == |desired_target_compare_|, and
1214 // - |args| is non-empty (if |require_args_| == true).
1215 bool Match(const base::FilePath
& target_path
, const string16
& args
) const;
1217 // A convenience routine to create a callback to call Match().
1218 // The callback is only valid during the lifetime of the FilterTargetEq
1220 ShortcutFilterCallback
AsShortcutFilterCallback();
1223 InstallUtil::ProgramCompare desired_target_compare_
;
1228 FilterTargetEq::FilterTargetEq(const base::FilePath
& desired_target_exe
,
1230 : desired_target_compare_(desired_target_exe
),
1231 require_args_(require_args
) {}
1233 bool FilterTargetEq::Match(const base::FilePath
& target_path
,
1234 const string16
& args
) const {
1235 if (!desired_target_compare_
.EvaluatePath(target_path
))
1237 if (require_args_
&& args
.empty())
1242 ShortcutFilterCallback
FilterTargetEq::AsShortcutFilterCallback() {
1243 return base::Bind(&FilterTargetEq::Match
, base::Unretained(this));
1246 // Shortcut operations for BatchShortcutAction().
1248 typedef base::Callback
<bool(const base::FilePath
& /*shortcut_path*/)>
1249 ShortcutOperationCallback
;
1251 bool ShortcutOpUnpin(const base::FilePath
& shortcut_path
) {
1252 VLOG(1) << "Trying to unpin " << shortcut_path
.value();
1253 if (!base::win::TaskbarUnpinShortcutLink(shortcut_path
.value().c_str())) {
1254 VLOG(1) << shortcut_path
.value() << " wasn't pinned (or the unpin failed).";
1255 // No error, since shortcut might not be pinned.
1260 bool ShortcutOpDelete(const base::FilePath
& shortcut_path
) {
1261 bool ret
= base::DeleteFile(shortcut_path
, false);
1262 LOG_IF(ERROR
, !ret
) << "Failed to remove " << shortcut_path
.value();
1266 bool ShortcutOpUpdate(const base::win::ShortcutProperties
& shortcut_properties
,
1267 const base::FilePath
& shortcut_path
) {
1268 bool ret
= base::win::CreateOrUpdateShortcutLink(
1269 shortcut_path
, shortcut_properties
, base::win::SHORTCUT_UPDATE_EXISTING
);
1270 LOG_IF(ERROR
, !ret
) << "Failed to update " << shortcut_path
.value();
1274 // {|location|, |dist|, |level|} determine |shortcut_folder|.
1275 // For each shortcut in |shortcut_folder| that match |shortcut_filter|, apply
1276 // |shortcut_operation|. Returns true if all operations are successful.
1277 // All intended operations are attempted, even if failures occur.
1278 bool BatchShortcutAction(const ShortcutFilterCallback
& shortcut_filter
,
1279 const ShortcutOperationCallback
& shortcut_operation
,
1280 ShellUtil::ShortcutLocation location
,
1281 BrowserDistribution
* dist
,
1282 ShellUtil::ShellChange level
) {
1283 DCHECK(!shortcut_operation
.is_null());
1284 base::FilePath shortcut_folder
;
1285 if (!ShellUtil::GetShortcutPath(location
, dist
, level
, &shortcut_folder
)) {
1286 LOG(WARNING
) << "Cannot find path at location " << location
;
1290 bool success
= true;
1291 base::FileEnumerator
enumerator(
1292 shortcut_folder
, false, base::FileEnumerator::FILES
,
1293 string16(L
"*") + installer::kLnkExt
);
1294 base::FilePath target_path
;
1296 for (base::FilePath shortcut_path
= enumerator
.Next();
1297 !shortcut_path
.empty();
1298 shortcut_path
= enumerator
.Next()) {
1299 if (base::win::ResolveShortcut(shortcut_path
, &target_path
, &args
)) {
1300 if (shortcut_filter
.Run(target_path
, args
) &&
1301 !shortcut_operation
.Run(shortcut_path
)) {
1305 LOG(ERROR
) << "Cannot resolve shortcut at " << shortcut_path
.value();
1313 // If the folder specified by {|location|, |dist|, |level|} is empty, remove it.
1314 // Otherwise do nothing. Returns true on success, including the vacuous case
1315 // where no deletion occurred because directory is non-empty.
1316 bool RemoveShortcutFolderIfEmpty(ShellUtil::ShortcutLocation location
,
1317 BrowserDistribution
* dist
,
1318 ShellUtil::ShellChange level
) {
1319 // Explicitly whitelist locations, since accidental calls can be very harmful.
1320 if (location
!= ShellUtil::SHORTCUT_LOCATION_START_MENU
&&
1321 location
!= ShellUtil::SHORTCUT_LOCATION_APP_SHORTCUTS
) {
1326 base::FilePath shortcut_folder
;
1327 if (!ShellUtil::GetShortcutPath(location
, dist
, level
, &shortcut_folder
)) {
1328 LOG(WARNING
) << "Cannot find path at location " << location
;
1331 if (file_util::IsDirectoryEmpty(shortcut_folder
) &&
1332 !base::DeleteFile(shortcut_folder
, true)) {
1333 LOG(ERROR
) << "Cannot remove folder " << shortcut_folder
.value();
1341 const wchar_t* ShellUtil::kRegDefaultIcon
= L
"\\DefaultIcon";
1342 const wchar_t* ShellUtil::kRegShellPath
= L
"\\shell";
1343 const wchar_t* ShellUtil::kRegShellOpen
= L
"\\shell\\open\\command";
1344 const wchar_t* ShellUtil::kRegStartMenuInternet
=
1345 L
"Software\\Clients\\StartMenuInternet";
1346 const wchar_t* ShellUtil::kRegClasses
= L
"Software\\Classes";
1347 const wchar_t* ShellUtil::kRegRegisteredApplications
=
1348 L
"Software\\RegisteredApplications";
1349 const wchar_t* ShellUtil::kRegVistaUrlPrefs
=
1350 L
"Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
1351 L
"http\\UserChoice";
1352 const wchar_t* ShellUtil::kAppPathsRegistryKey
=
1353 L
"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths";
1354 const wchar_t* ShellUtil::kAppPathsRegistryPathName
= L
"Path";
1356 #if defined(GOOGLE_CHROME_BUILD)
1357 const wchar_t* ShellUtil::kChromeHTMLProgId
= L
"ChromeHTML";
1358 const wchar_t* ShellUtil::kChromeHTMLProgIdDesc
= L
"Chrome HTML Document";
1360 // This used to be "ChromiumHTML", but was forced to become "ChromiumHTM"
1361 // because of http://crbug.com/153349 as with the '.' and 26 characters suffix
1362 // added on user-level installs, the generated progid for Chromium was 39
1363 // characters long which, according to MSDN (
1364 // http://msdn.microsoft.com/library/aa911706.aspx), is the maximum length
1365 // for a progid. It was however determined through experimentation that the 39
1366 // character limit mentioned on MSDN includes the NULL character...
1367 const wchar_t* ShellUtil::kChromeHTMLProgId
= L
"ChromiumHTM";
1368 const wchar_t* ShellUtil::kChromeHTMLProgIdDesc
= L
"Chromium HTML Document";
1371 const wchar_t* ShellUtil::kDefaultFileAssociations
[] = {L
".htm", L
".html",
1372 L
".shtml", L
".xht", L
".xhtml", NULL
};
1373 const wchar_t* ShellUtil::kPotentialFileAssociations
[] = {L
".htm", L
".html",
1374 L
".shtml", L
".xht", L
".xhtml", L
".webp", NULL
};
1375 const wchar_t* ShellUtil::kBrowserProtocolAssociations
[] = {L
"ftp", L
"http",
1377 const wchar_t* ShellUtil::kPotentialProtocolAssociations
[] = {L
"ftp", L
"http",
1378 L
"https", L
"irc", L
"mailto", L
"mms", L
"news", L
"nntp", L
"sms", L
"smsto",
1379 L
"tel", L
"urn", L
"webcal", NULL
};
1380 const wchar_t* ShellUtil::kRegUrlProtocol
= L
"URL Protocol";
1381 const wchar_t* ShellUtil::kRegApplication
= L
"\\Application";
1382 const wchar_t* ShellUtil::kRegAppUserModelId
= L
"AppUserModelId";
1383 const wchar_t* ShellUtil::kRegApplicationDescription
=
1384 L
"ApplicationDescription";
1385 const wchar_t* ShellUtil::kRegApplicationName
= L
"ApplicationName";
1386 const wchar_t* ShellUtil::kRegApplicationIcon
= L
"ApplicationIcon";
1387 const wchar_t* ShellUtil::kRegApplicationCompany
= L
"ApplicationCompany";
1388 const wchar_t* ShellUtil::kRegExePath
= L
"\\.exe";
1389 const wchar_t* ShellUtil::kRegVerbOpen
= L
"open";
1390 const wchar_t* ShellUtil::kRegVerbOpenNewWindow
= L
"opennewwindow";
1391 const wchar_t* ShellUtil::kRegVerbRun
= L
"run";
1392 const wchar_t* ShellUtil::kRegCommand
= L
"command";
1393 const wchar_t* ShellUtil::kRegDelegateExecute
= L
"DelegateExecute";
1394 const wchar_t* ShellUtil::kRegOpenWithProgids
= L
"OpenWithProgids";
1396 bool ShellUtil::QuickIsChromeRegisteredInHKLM(BrowserDistribution
* dist
,
1397 const string16
& chrome_exe
,
1398 const string16
& suffix
) {
1399 return QuickIsChromeRegistered(dist
, chrome_exe
, suffix
,
1400 CONFIRM_SHELL_REGISTRATION_IN_HKLM
);
1403 bool ShellUtil::ShortcutLocationIsSupported(
1404 ShellUtil::ShortcutLocation location
) {
1406 case SHORTCUT_LOCATION_DESKTOP
:
1408 case SHORTCUT_LOCATION_QUICK_LAUNCH
:
1410 case SHORTCUT_LOCATION_START_MENU
:
1412 case SHORTCUT_LOCATION_TASKBAR_PINS
:
1413 return base::win::GetVersion() >= base::win::VERSION_WIN7
;
1414 case SHORTCUT_LOCATION_APP_SHORTCUTS
:
1415 return base::win::GetVersion() >= base::win::VERSION_WIN8
;
1422 bool ShellUtil::GetShortcutPath(ShellUtil::ShortcutLocation location
,
1423 BrowserDistribution
* dist
,
1425 base::FilePath
* path
) {
1428 bool add_folder_for_dist
= false;
1430 case SHORTCUT_LOCATION_DESKTOP
:
1431 dir_key
= (level
== CURRENT_USER
) ? base::DIR_USER_DESKTOP
:
1432 base::DIR_COMMON_DESKTOP
;
1434 case SHORTCUT_LOCATION_QUICK_LAUNCH
:
1435 dir_key
= (level
== CURRENT_USER
) ? base::DIR_USER_QUICK_LAUNCH
:
1436 base::DIR_DEFAULT_USER_QUICK_LAUNCH
;
1438 case SHORTCUT_LOCATION_START_MENU
:
1439 dir_key
= (level
== CURRENT_USER
) ? base::DIR_START_MENU
:
1440 base::DIR_COMMON_START_MENU
;
1441 add_folder_for_dist
= true;
1443 case SHORTCUT_LOCATION_TASKBAR_PINS
:
1444 dir_key
= base::DIR_TASKBAR_PINS
;
1446 case SHORTCUT_LOCATION_APP_SHORTCUTS
:
1447 // TODO(huangs): Move GetAppShortcutsFolder() logic into base_paths_win.
1448 return GetAppShortcutsFolder(dist
, level
, path
);
1455 if (!PathService::Get(dir_key
, path
) || path
->empty()) {
1456 NOTREACHED() << dir_key
;
1460 if (add_folder_for_dist
) {
1461 *path
= path
->Append(dist
->GetStartMenuShortcutSubfolder(
1462 BrowserDistribution::SUBFOLDER_CHROME
));
1468 bool ShellUtil::CreateOrUpdateShortcut(
1469 ShellUtil::ShortcutLocation location
,
1470 BrowserDistribution
* dist
,
1471 const ShellUtil::ShortcutProperties
& properties
,
1472 ShellUtil::ShortcutOperation operation
) {
1473 // Explicitly whitelist locations to which this is applicable.
1474 if (location
!= SHORTCUT_LOCATION_DESKTOP
&&
1475 location
!= SHORTCUT_LOCATION_QUICK_LAUNCH
&&
1476 location
!= SHORTCUT_LOCATION_START_MENU
) {
1482 // |pin_to_taskbar| is only acknowledged when first creating the shortcut.
1483 DCHECK(!properties
.pin_to_taskbar
||
1484 operation
== SHELL_SHORTCUT_CREATE_ALWAYS
||
1485 operation
== SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL
);
1487 base::FilePath user_shortcut_path
;
1488 base::FilePath system_shortcut_path
;
1489 if (!GetShortcutPath(location
, dist
, SYSTEM_LEVEL
, &system_shortcut_path
)) {
1494 string16
shortcut_name(ExtractShortcutNameFromProperties(dist
, properties
));
1495 system_shortcut_path
= system_shortcut_path
.Append(shortcut_name
);
1497 base::FilePath
* chosen_path
;
1498 bool should_install_shortcut
= true;
1499 if (properties
.level
== SYSTEM_LEVEL
) {
1500 // Install the system-level shortcut if requested.
1501 chosen_path
= &system_shortcut_path
;
1502 } else if (operation
!= SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL
||
1503 !base::PathExists(system_shortcut_path
)) {
1504 // Otherwise install the user-level shortcut, unless the system-level
1505 // variant of this shortcut is present on the machine and |operation| states
1506 // not to create a user-level shortcut in that case.
1507 if (!GetShortcutPath(location
, dist
, CURRENT_USER
, &user_shortcut_path
)) {
1511 user_shortcut_path
= user_shortcut_path
.Append(shortcut_name
);
1512 chosen_path
= &user_shortcut_path
;
1514 // Do not install any shortcut if we are told to install a user-level
1515 // shortcut, but the system-level variant of that shortcut is present.
1516 // Other actions (e.g., pinning) can still happen with respect to the
1517 // existing system-level shortcut however.
1518 chosen_path
= &system_shortcut_path
;
1519 should_install_shortcut
= false;
1522 if (chosen_path
== NULL
|| chosen_path
->empty()) {
1527 base::win::ShortcutOperation shortcut_operation
=
1528 TranslateShortcutOperation(operation
);
1530 if (should_install_shortcut
) {
1531 // Make sure the parent directories exist when creating the shortcut.
1532 if (shortcut_operation
== base::win::SHORTCUT_CREATE_ALWAYS
&&
1533 !file_util::CreateDirectory(chosen_path
->DirName())) {
1538 base::win::ShortcutProperties
shortcut_properties(
1539 TranslateShortcutProperties(properties
));
1540 ret
= base::win::CreateOrUpdateShortcutLink(
1541 *chosen_path
, shortcut_properties
, shortcut_operation
);
1544 if (ret
&& shortcut_operation
== base::win::SHORTCUT_CREATE_ALWAYS
&&
1545 properties
.pin_to_taskbar
&&
1546 base::win::GetVersion() >= base::win::VERSION_WIN7
) {
1547 ret
= base::win::TaskbarPinShortcutLink(chosen_path
->value().c_str());
1549 LOG(ERROR
) << "Failed to pin " << chosen_path
->value();
1556 string16
ShellUtil::FormatIconLocation(const string16
& icon_path
,
1558 string16
icon_string(icon_path
);
1559 icon_string
.append(L
",");
1560 icon_string
.append(base::IntToString16(icon_index
));
1564 string16
ShellUtil::GetChromeShellOpenCmd(const string16
& chrome_exe
) {
1565 return L
"\"" + chrome_exe
+ L
"\" -- \"%1\"";
1568 string16
ShellUtil::GetChromeDelegateCommand(const string16
& chrome_exe
) {
1569 return L
"\"" + chrome_exe
+ L
"\" -- %*";
1572 void ShellUtil::GetRegisteredBrowsers(
1573 BrowserDistribution
* dist
,
1574 std::map
<string16
, string16
>* browsers
) {
1578 const string16
base_key(ShellUtil::kRegStartMenuInternet
);
1579 string16 client_path
;
1584 // HKCU has precedence over HKLM for these registrations: http://goo.gl/xjczJ.
1585 // Look in HKCU second to override any identical values found in HKLM.
1586 const HKEY roots
[] = { HKEY_LOCAL_MACHINE
, HKEY_CURRENT_USER
};
1587 for (int i
= 0; i
< arraysize(roots
); ++i
) {
1588 const HKEY root
= roots
[i
];
1589 for (base::win::RegistryKeyIterator
iter(root
, base_key
.c_str());
1590 iter
.Valid(); ++iter
) {
1591 client_path
.assign(base_key
).append(1, L
'\\').append(iter
.Name());
1592 // Read the browser's name (localized according to install language).
1593 if (key
.Open(root
, client_path
.c_str(),
1594 KEY_QUERY_VALUE
) != ERROR_SUCCESS
||
1595 key
.ReadValue(NULL
, &name
) != ERROR_SUCCESS
||
1597 name
.find(dist
->GetBaseAppName()) != string16::npos
) {
1600 // Read the browser's reinstall command.
1601 if (key
.Open(root
, (client_path
+ L
"\\InstallInfo").c_str(),
1602 KEY_QUERY_VALUE
) == ERROR_SUCCESS
&&
1603 key
.ReadValue(kReinstallCommand
, &command
) == ERROR_SUCCESS
&&
1605 (*browsers
)[name
] = command
;
1611 string16
ShellUtil::GetCurrentInstallationSuffix(BrowserDistribution
* dist
,
1612 const string16
& chrome_exe
) {
1613 // This method is somewhat the opposite of GetInstallationSpecificSuffix().
1614 // In this case we are not trying to determine the current suffix for the
1615 // upcoming installation (i.e. not trying to stick to a currently bad
1616 // registration style if one is present).
1617 // Here we want to determine which suffix we should use at run-time.
1618 // In order of preference, we prefer (for user-level installs):
1619 // 1) Base 32 encoding of the md5 hash of the user's sid (new-style).
1620 // 2) Username (old-style).
1621 // 3) Unsuffixed (even worse).
1622 string16 tested_suffix
;
1623 if (InstallUtil::IsPerUserInstall(chrome_exe
.c_str()) &&
1624 (!GetUserSpecificRegistrySuffix(&tested_suffix
) ||
1625 !QuickIsChromeRegistered(dist
, chrome_exe
, tested_suffix
,
1626 CONFIRM_PROGID_REGISTRATION
)) &&
1627 (!GetOldUserSpecificRegistrySuffix(&tested_suffix
) ||
1628 !QuickIsChromeRegistered(dist
, chrome_exe
, tested_suffix
,
1629 CONFIRM_PROGID_REGISTRATION
)) &&
1630 !QuickIsChromeRegistered(dist
, chrome_exe
, tested_suffix
.erase(),
1631 CONFIRM_PROGID_REGISTRATION
)) {
1632 // If Chrome is not registered under any of the possible suffixes (e.g.
1633 // tests, Canary, etc.): use the new-style suffix at run-time.
1634 if (!GetUserSpecificRegistrySuffix(&tested_suffix
))
1637 return tested_suffix
;
1640 string16
ShellUtil::GetApplicationName(BrowserDistribution
* dist
,
1641 const string16
& chrome_exe
) {
1642 string16 app_name
= dist
->GetBaseAppName();
1643 app_name
+= GetCurrentInstallationSuffix(dist
, chrome_exe
);
1647 string16
ShellUtil::GetBrowserModelId(BrowserDistribution
* dist
,
1648 bool is_per_user_install
) {
1649 string16
app_id(dist
->GetBaseAppId());
1652 // TODO(robertshield): Temporary hack to make the kRegisterChromeBrowserSuffix
1653 // apply to all registry values computed down in these murky depths.
1654 CommandLine
& command_line
= *CommandLine::ForCurrentProcess();
1655 if (command_line
.HasSwitch(
1656 installer::switches::kRegisterChromeBrowserSuffix
)) {
1657 suffix
= command_line
.GetSwitchValueNative(
1658 installer::switches::kRegisterChromeBrowserSuffix
);
1659 } else if (is_per_user_install
&& !GetUserSpecificRegistrySuffix(&suffix
)) {
1662 // There is only one component (i.e. the suffixed appid) in this case, but it
1663 // is still necessary to go through the appid constructor to make sure the
1664 // returned appid is truncated if necessary.
1665 std::vector
<string16
> components(1, app_id
.append(suffix
));
1666 return BuildAppModelId(components
);
1669 string16
ShellUtil::BuildAppModelId(
1670 const std::vector
<string16
>& components
) {
1671 DCHECK_GT(components
.size(), 0U);
1673 // Find the maximum numbers of characters allowed in each component
1674 // (accounting for the dots added between each component).
1675 const size_t available_chars
=
1676 installer::kMaxAppModelIdLength
- (components
.size() - 1);
1677 const size_t max_component_length
= available_chars
/ components
.size();
1679 // |max_component_length| should be at least 2; otherwise the truncation logic
1681 if (max_component_length
< 2U) {
1683 return (*components
.begin()).substr(0, installer::kMaxAppModelIdLength
);
1687 app_id
.reserve(installer::kMaxAppModelIdLength
);
1688 for (std::vector
<string16
>::const_iterator it
= components
.begin();
1689 it
!= components
.end(); ++it
) {
1690 if (it
!= components
.begin())
1691 app_id
.push_back(L
'.');
1693 const string16
& component
= *it
;
1694 DCHECK(!component
.empty());
1695 if (component
.length() > max_component_length
) {
1696 // Append a shortened version of this component. Cut in the middle to try
1697 // to avoid losing the unique parts of this component (which are usually
1698 // at the beginning or end for things like usernames and paths).
1699 app_id
.append(component
.c_str(), 0, max_component_length
/ 2);
1700 app_id
.append(component
.c_str(),
1701 component
.length() - ((max_component_length
+ 1) / 2),
1704 app_id
.append(component
);
1707 // No spaces are allowed in the AppUserModelId according to MSDN.
1708 ReplaceChars(app_id
, L
" ", L
"_", &app_id
);
1712 ShellUtil::DefaultState
ShellUtil::GetChromeDefaultState() {
1713 // When we check for default browser we don't necessarily want to count file
1714 // type handlers and icons as having changed the default browser status,
1715 // since the user may have changed their shell settings to cause HTML files
1716 // to open with a text editor for example. We also don't want to aggressively
1717 // claim FTP, since the user may have a separate FTP client. It is an open
1718 // question as to how to "heal" these settings. Perhaps the user should just
1719 // re-run the installer or run with the --set-default-browser command line
1720 // flag. There is doubtless some other key we can hook into to cause "Repair"
1721 // to show up in Add/Remove programs for us.
1722 static const wchar_t* const kChromeProtocols
[] = { L
"http", L
"https" };
1723 return ProbeProtocolHandlers(kChromeProtocols
, arraysize(kChromeProtocols
));
1726 ShellUtil::DefaultState
ShellUtil::GetChromeDefaultProtocolClientState(
1727 const string16
& protocol
) {
1728 if (protocol
.empty())
1729 return UNKNOWN_DEFAULT
;
1731 const wchar_t* const protocols
[] = { protocol
.c_str() };
1732 return ProbeProtocolHandlers(protocols
, arraysize(protocols
));
1736 bool ShellUtil::CanMakeChromeDefaultUnattended() {
1737 return base::win::GetVersion() < base::win::VERSION_WIN8
;
1740 bool ShellUtil::MakeChromeDefault(BrowserDistribution
* dist
,
1742 const string16
& chrome_exe
,
1743 bool elevate_if_not_admin
) {
1744 DCHECK(!(shell_change
& ShellUtil::SYSTEM_LEVEL
) || IsUserAnAdmin());
1746 if (!dist
->CanSetAsDefault())
1749 // Windows 8 does not permit making a browser default just like that.
1750 // This process needs to be routed through the system's UI. Use
1751 // ShowMakeChromeDefaultSystemUI instead (below).
1752 if (!CanMakeChromeDefaultUnattended()) {
1756 if (!ShellUtil::RegisterChromeBrowser(
1757 dist
, chrome_exe
, string16(), elevate_if_not_admin
)) {
1762 // First use the new "recommended" way on Vista to make Chrome default
1764 string16 app_name
= GetApplicationName(dist
, chrome_exe
);
1766 if (base::win::GetVersion() >= base::win::VERSION_VISTA
) {
1767 // On Windows Vista and Win7 we still can set ourselves via the
1768 // the IApplicationAssociationRegistration interface.
1769 VLOG(1) << "Registering Chrome as default browser on Vista.";
1770 base::win::ScopedComPtr
<IApplicationAssociationRegistration
> pAAR
;
1771 HRESULT hr
= pAAR
.CreateInstance(CLSID_ApplicationAssociationRegistration
,
1772 NULL
, CLSCTX_INPROC
);
1773 if (SUCCEEDED(hr
)) {
1774 for (int i
= 0; ShellUtil::kBrowserProtocolAssociations
[i
] != NULL
; i
++) {
1775 hr
= pAAR
->SetAppAsDefault(app_name
.c_str(),
1776 ShellUtil::kBrowserProtocolAssociations
[i
], AT_URLPROTOCOL
);
1777 if (!SUCCEEDED(hr
)) {
1779 LOG(ERROR
) << "Failed to register as default for protocol "
1780 << ShellUtil::kBrowserProtocolAssociations
[i
]
1781 << " (" << hr
<< ")";
1785 for (int i
= 0; ShellUtil::kDefaultFileAssociations
[i
] != NULL
; i
++) {
1786 hr
= pAAR
->SetAppAsDefault(app_name
.c_str(),
1787 ShellUtil::kDefaultFileAssociations
[i
], AT_FILEEXTENSION
);
1788 if (!SUCCEEDED(hr
)) {
1790 LOG(ERROR
) << "Failed to register as default for file extension "
1791 << ShellUtil::kDefaultFileAssociations
[i
]
1792 << " (" << hr
<< ")";
1798 if (!RegisterChromeAsDefaultXPStyle(dist
, shell_change
, chrome_exe
))
1801 // Send Windows notification event so that it can update icons for
1802 // file associations.
1803 SHChangeNotify(SHCNE_ASSOCCHANGED
, SHCNF_IDLIST
, NULL
, NULL
);
1807 bool ShellUtil::ShowMakeChromeDefaultSystemUI(BrowserDistribution
* dist
,
1808 const string16
& chrome_exe
) {
1809 DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8
);
1810 if (!dist
->CanSetAsDefault())
1813 if (!RegisterChromeBrowser(dist
, chrome_exe
, string16(), true))
1816 bool succeeded
= true;
1817 bool is_default
= (GetChromeDefaultState() == IS_DEFAULT
);
1819 // On Windows 8, you can't set yourself as the default handler
1820 // programatically. In other words IApplicationAssociationRegistration
1821 // has been rendered useless. What you can do is to launch
1822 // "Set Program Associations" section of the "Default Programs"
1823 // control panel, which is a mess, or pop the concise "How you want to open
1824 // webpages?" dialog. We choose the latter.
1825 succeeded
= LaunchSelectDefaultProtocolHandlerDialog(L
"http");
1826 is_default
= (succeeded
&& GetChromeDefaultState() == IS_DEFAULT
);
1828 if (succeeded
&& is_default
)
1829 RegisterChromeAsDefaultXPStyle(dist
, CURRENT_USER
, chrome_exe
);
1833 bool ShellUtil::MakeChromeDefaultProtocolClient(BrowserDistribution
* dist
,
1834 const string16
& chrome_exe
,
1835 const string16
& protocol
) {
1836 if (!dist
->CanSetAsDefault())
1839 if (!RegisterChromeForProtocol(dist
, chrome_exe
, string16(), protocol
, true))
1842 // Windows 8 does not permit making a browser default just like that.
1843 // This process needs to be routed through the system's UI. Use
1844 // ShowMakeChromeDefaultProocolClientSystemUI instead (below).
1845 if (!CanMakeChromeDefaultUnattended())
1849 // First use the new "recommended" way on Vista to make Chrome default
1850 // protocol handler.
1851 if (base::win::GetVersion() >= base::win::VERSION_VISTA
) {
1852 VLOG(1) << "Registering Chrome as default handler for " << protocol
1854 base::win::ScopedComPtr
<IApplicationAssociationRegistration
> pAAR
;
1855 HRESULT hr
= pAAR
.CreateInstance(CLSID_ApplicationAssociationRegistration
,
1856 NULL
, CLSCTX_INPROC
);
1857 if (SUCCEEDED(hr
)) {
1858 string16 app_name
= GetApplicationName(dist
, chrome_exe
);
1859 hr
= pAAR
->SetAppAsDefault(app_name
.c_str(), protocol
.c_str(),
1862 if (!SUCCEEDED(hr
)) {
1864 LOG(ERROR
) << "Could not make Chrome default protocol client (Vista):"
1865 << " HRESULT=" << hr
<< ".";
1869 // Now use the old way to associate Chrome with the desired protocol. This
1870 // should not be required on Vista+, but since some applications still read
1871 // Software\Classes\<protocol> key directly, do this on Vista+ also.
1872 if (!RegisterChromeAsDefaultProtocolClientXPStyle(dist
, chrome_exe
, protocol
))
1878 bool ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(
1879 BrowserDistribution
* dist
,
1880 const string16
& chrome_exe
,
1881 const string16
& protocol
) {
1882 DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8
);
1883 if (!dist
->CanSetAsDefault())
1886 if (!RegisterChromeForProtocol(dist
, chrome_exe
, string16(), protocol
, true))
1889 bool succeeded
= true;
1891 GetChromeDefaultProtocolClientState(protocol
) == IS_DEFAULT
);
1893 // On Windows 8, you can't set yourself as the default handler
1894 // programatically. In other words IApplicationAssociationRegistration
1895 // has been rendered useless. What you can do is to launch
1896 // "Set Program Associations" section of the "Default Programs"
1897 // control panel, which is a mess, or pop the concise "How you want to open
1898 // links of this type (protocol)?" dialog. We choose the latter.
1899 succeeded
= LaunchSelectDefaultProtocolHandlerDialog(protocol
.c_str());
1900 is_default
= (succeeded
&&
1901 GetChromeDefaultProtocolClientState(protocol
) == IS_DEFAULT
);
1903 if (succeeded
&& is_default
)
1904 RegisterChromeAsDefaultProtocolClientXPStyle(dist
, chrome_exe
, protocol
);
1908 bool ShellUtil::RegisterChromeBrowser(BrowserDistribution
* dist
,
1909 const string16
& chrome_exe
,
1910 const string16
& unique_suffix
,
1911 bool elevate_if_not_admin
) {
1912 if (!dist
->CanSetAsDefault())
1915 CommandLine
& command_line
= *CommandLine::ForCurrentProcess();
1918 if (!unique_suffix
.empty()) {
1919 suffix
= unique_suffix
;
1920 } else if (command_line
.HasSwitch(
1921 installer::switches::kRegisterChromeBrowserSuffix
)) {
1922 suffix
= command_line
.GetSwitchValueNative(
1923 installer::switches::kRegisterChromeBrowserSuffix
);
1924 } else if (!GetInstallationSpecificSuffix(dist
, chrome_exe
, &suffix
)) {
1928 RemoveRunVerbOnWindows8(dist
, chrome_exe
);
1930 bool user_level
= InstallUtil::IsPerUserInstall(chrome_exe
.c_str());
1931 HKEY root
= DetermineRegistrationRoot(user_level
);
1933 // Look only in HKLM for system-level installs (otherwise, if a user-level
1934 // install is also present, it will lead IsChromeRegistered() to think this
1935 // system-level install isn't registered properly as it is shadowed by the
1936 // user-level install's registrations).
1937 uint32 look_for_in
= user_level
?
1938 RegistryEntry::LOOK_IN_HKCU_THEN_HKLM
: RegistryEntry::LOOK_IN_HKLM
;
1940 // Check if chrome is already registered with this suffix.
1941 if (IsChromeRegistered(dist
, chrome_exe
, suffix
, look_for_in
))
1945 if (root
== HKEY_CURRENT_USER
|| IsUserAnAdmin()) {
1946 // Do the full registration if we can do it at user-level or if the user is
1948 ScopedVector
<RegistryEntry
> progid_and_appreg_entries
;
1949 ScopedVector
<RegistryEntry
> shell_entries
;
1950 RegistryEntry::GetProgIdEntries(dist
, chrome_exe
, suffix
,
1951 &progid_and_appreg_entries
);
1952 RegistryEntry::GetAppRegistrationEntries(chrome_exe
, suffix
,
1953 &progid_and_appreg_entries
);
1954 RegistryEntry::GetShellIntegrationEntries(
1955 dist
, chrome_exe
, suffix
, &shell_entries
);
1956 result
= (AddRegistryEntries(root
, progid_and_appreg_entries
) &&
1957 AddRegistryEntries(root
, shell_entries
));
1958 } else if (elevate_if_not_admin
&&
1959 base::win::GetVersion() >= base::win::VERSION_VISTA
&&
1960 ElevateAndRegisterChrome(dist
, chrome_exe
, suffix
, L
"")) {
1961 // If the user is not an admin and OS is between Vista and Windows 7
1962 // inclusively, try to elevate and register. This is only intended for
1963 // user-level installs as system-level installs should always be run with
1967 // If we got to this point then all we can do is create ProgId and basic app
1968 // registrations under HKCU.
1969 ScopedVector
<RegistryEntry
> entries
;
1970 RegistryEntry::GetProgIdEntries(dist
, chrome_exe
, string16(), &entries
);
1971 // Prefer to use |suffix|; unless Chrome's ProgIds are already registered
1972 // with no suffix (as per the old registration style): in which case some
1973 // other registry entries could refer to them and since we were not able to
1974 // set our HKLM entries above, we are better off not altering these here.
1975 if (!AreEntriesRegistered(entries
, RegistryEntry::LOOK_IN_HKCU
)) {
1976 if (!suffix
.empty()) {
1978 RegistryEntry::GetProgIdEntries(dist
, chrome_exe
, suffix
, &entries
);
1979 RegistryEntry::GetAppRegistrationEntries(chrome_exe
, suffix
, &entries
);
1981 result
= AddRegistryEntries(HKEY_CURRENT_USER
, entries
);
1983 // The ProgId is registered unsuffixed in HKCU, also register the app with
1984 // Windows in HKCU (this was not done in the old registration style and
1985 // thus needs to be done after the above check for the unsuffixed
1988 RegistryEntry::GetAppRegistrationEntries(chrome_exe
, string16(),
1990 result
= AddRegistryEntries(HKEY_CURRENT_USER
, entries
);
1993 SHChangeNotify(SHCNE_ASSOCCHANGED
, SHCNF_IDLIST
, NULL
, NULL
);
1997 bool ShellUtil::RegisterChromeForProtocol(BrowserDistribution
* dist
,
1998 const string16
& chrome_exe
,
1999 const string16
& unique_suffix
,
2000 const string16
& protocol
,
2001 bool elevate_if_not_admin
) {
2002 if (!dist
->CanSetAsDefault())
2006 if (!unique_suffix
.empty()) {
2007 suffix
= unique_suffix
;
2008 } else if (!GetInstallationSpecificSuffix(dist
, chrome_exe
, &suffix
)) {
2012 bool user_level
= InstallUtil::IsPerUserInstall(chrome_exe
.c_str());
2013 HKEY root
= DetermineRegistrationRoot(user_level
);
2015 // Look only in HKLM for system-level installs (otherwise, if a user-level
2016 // install is also present, it could lead IsChromeRegisteredForProtocol() to
2017 // think this system-level install isn't registered properly as it may be
2018 // shadowed by the user-level install's registrations).
2019 uint32 look_for_in
= user_level
?
2020 RegistryEntry::LOOK_IN_HKCU_THEN_HKLM
: RegistryEntry::LOOK_IN_HKLM
;
2022 // Check if chrome is already registered with this suffix.
2023 if (IsChromeRegisteredForProtocol(dist
, suffix
, protocol
, look_for_in
))
2026 if (root
== HKEY_CURRENT_USER
|| IsUserAnAdmin()) {
2027 // We can do this operation directly.
2028 // First, make sure Chrome is fully registered on this machine.
2029 if (!RegisterChromeBrowser(dist
, chrome_exe
, suffix
, false))
2032 // Write in the capabillity for the protocol.
2033 ScopedVector
<RegistryEntry
> entries
;
2034 RegistryEntry::GetProtocolCapabilityEntries(dist
, suffix
, protocol
,
2036 return AddRegistryEntries(root
, entries
);
2037 } else if (elevate_if_not_admin
&&
2038 base::win::GetVersion() >= base::win::VERSION_VISTA
) {
2039 // Elevate to do the whole job
2040 return ElevateAndRegisterChrome(dist
, chrome_exe
, suffix
, protocol
);
2042 // Admin rights are required to register capabilities before Windows 8.
2048 bool ShellUtil::RemoveShortcuts(ShellUtil::ShortcutLocation location
,
2049 BrowserDistribution
* dist
,
2051 const base::FilePath
& target_exe
) {
2052 if (!ShellUtil::ShortcutLocationIsSupported(location
))
2053 return true; // Vacuous success.
2055 FilterTargetEq
shortcut_filter(target_exe
, false);
2056 // Main operation to apply to each shortcut in the directory specified.
2057 ShortcutOperationCallback
shortcut_operation(
2058 location
== SHORTCUT_LOCATION_TASKBAR_PINS
?
2059 base::Bind(&ShortcutOpUnpin
) : base::Bind(&ShortcutOpDelete
));
2060 bool success
= BatchShortcutAction(shortcut_filter
.AsShortcutFilterCallback(),
2061 shortcut_operation
, location
, dist
, level
);
2062 // Remove chrome-specific shortcut folders if they are now empty.
2064 (location
== SHORTCUT_LOCATION_START_MENU
||
2065 location
== SHORTCUT_LOCATION_APP_SHORTCUTS
)) {
2066 success
= RemoveShortcutFolderIfEmpty(location
, dist
, level
);
2072 bool ShellUtil::UpdateShortcuts(
2073 ShellUtil::ShortcutLocation location
,
2074 BrowserDistribution
* dist
,
2076 const base::FilePath
& target_exe
,
2078 const ShellUtil::ShortcutProperties
& properties
) {
2079 if (!ShellUtil::ShortcutLocationIsSupported(location
))
2080 return true; // Vacuous success.
2082 FilterTargetEq
shortcut_filter(target_exe
, require_args
);
2083 ShortcutOperationCallback
shortcut_operation(
2084 base::Bind(&ShortcutOpUpdate
, TranslateShortcutProperties(properties
)));
2085 return BatchShortcutAction(shortcut_filter
.AsShortcutFilterCallback(),
2086 shortcut_operation
, location
, dist
, level
);
2089 bool ShellUtil::GetUserSpecificRegistrySuffix(string16
* suffix
) {
2090 // Use a thread-safe cache for the user's suffix.
2091 static base::LazyInstance
<UserSpecificRegistrySuffix
>::Leaky suffix_instance
=
2092 LAZY_INSTANCE_INITIALIZER
;
2093 return suffix_instance
.Get().GetSuffix(suffix
);
2096 bool ShellUtil::GetOldUserSpecificRegistrySuffix(string16
* suffix
) {
2097 wchar_t user_name
[256];
2098 DWORD size
= arraysize(user_name
);
2099 if (::GetUserName(user_name
, &size
) == 0 || size
< 1) {
2103 suffix
->reserve(size
);
2104 suffix
->assign(1, L
'.');
2105 suffix
->append(user_name
, size
- 1);
2109 string16
ShellUtil::ByteArrayToBase32(const uint8
* bytes
, size_t size
) {
2110 static const char kEncoding
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
2112 // Eliminate special cases first.
2115 } else if (size
== 1) {
2117 ret
.push_back(kEncoding
[(bytes
[0] & 0xf8) >> 3]);
2118 ret
.push_back(kEncoding
[(bytes
[0] & 0x07) << 2]);
2120 } else if (size
>= std::numeric_limits
<size_t>::max() / 8) {
2121 // If |size| is too big, the calculation of |encoded_length| below will
2127 // Overestimate the number of bits in the string by 4 so that dividing by 5
2128 // is the equivalent of rounding up the actual number of bits divided by 5.
2129 const size_t encoded_length
= (size
* 8 + 4) / 5;
2132 ret
.reserve(encoded_length
);
2134 // A bit stream which will be read from the left and appended to from the
2135 // right as it's emptied.
2136 uint16 bit_stream
= (bytes
[0] << 8) + bytes
[1];
2137 size_t next_byte_index
= 2;
2139 while (free_bits
< 16) {
2140 // Extract the 5 leftmost bits in the stream
2141 ret
.push_back(kEncoding
[(bit_stream
& 0xf800) >> 11]);
2145 // If there is enough room in the bit stream, inject another byte (if there
2146 // are any left...).
2147 if (free_bits
>= 8 && next_byte_index
< size
) {
2149 bit_stream
+= bytes
[next_byte_index
++] << free_bits
;
2153 DCHECK_EQ(ret
.length(), encoded_length
);