1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 // This file defines functions that integrate Chrome in Windows shell. These
6 // functions can be used by Chrome as well as Chrome installer. All of the
7 // work is done by the local functions defined in anonymous namespace in
8 // this class.
9
10 #include "chrome/installer/util/shell_util.h"
11
12 #include <objbase.h>
13 #include <shlobj.h>
14 #include <shobjidl.h>
15 #include <windows.h>
16 #include <wrl/client.h>
17
18 #include <algorithm>
19 #include <iterator>
20 #include <limits>
21 #include <memory>
22 #include <string>
23 #include <utility>
24
25 #include "base/bind.h"
26 #include "base/callback_helpers.h"
27 #include "base/command_line.h"
28 #include "base/feature_list.h"
29 #include "base/files/file_enumerator.h"
30 #include "base/files/file_path.h"
31 #include "base/files/file_util.h"
32 #include "base/hash/md5.h"
33 #include "base/lazy_instance.h"
34 #include "base/logging.h"
35 #include "base/metrics/histogram_functions.h"
36 #include "base/path_service.h"
37 #include "base/stl_util.h"
38 #include "base/strings/string16.h"
39 #include "base/strings/string_number_conversions.h"
40 #include "base/strings/string_piece.h"
41 #include "base/strings/string_split.h"
42 #include "base/strings/string_util.h"
43 #include "base/strings/stringprintf.h"
44 #include "base/strings/utf_string_conversions.h"
45 #include "base/synchronization/atomic_flag.h"
46 #include "base/values.h"
47 #include "base/win/registry.h"
48 #include "base/win/scoped_co_mem.h"
49 #include "base/win/shortcut.h"
50 #include "base/win/win_util.h"
51 #include "base/win/windows_version.h"
52 #include "chrome/common/chrome_constants.h"
53 #include "chrome/common/chrome_switches.h"
54 #include "chrome/install_static/install_constants.h"
55 #include "chrome/install_static/install_details.h"
56 #include "chrome/install_static/install_modes.h"
57 #include "chrome/install_static/install_util.h"
58 #include "chrome/installer/util/beacons.h"
59 #include "chrome/installer/util/helper.h"
60 #include "chrome/installer/util/initial_preferences.h"
61 #include "chrome/installer/util/initial_preferences_constants.h"
62 #include "chrome/installer/util/install_util.h"
63 #include "chrome/installer/util/installer_util_strings.h"
64 #include "chrome/installer/util/l10n_string_util.h"
65 #include "chrome/installer/util/registry_entry.h"
66 #include "chrome/installer/util/scoped_user_protocol_entry.h"
67 #include "chrome/installer/util/util_constants.h"
68 #include "chrome/installer/util/work_item.h"
69 #include "components/base32/base32.h"
70
71 using base::win::RegKey;
72
73 namespace {
74
75 // An enum used to tell QuickIsChromeRegistered() which level of registration
76 // the caller wants to confirm.
77 enum RegistrationConfirmationLevel {
78 // Only look for Chrome's ProgIds.
79 // This is sufficient when we are trying to determine the suffix of the
80 // currently running Chrome as shell integration registrations might not be
81 // present.
82 CONFIRM_PROGID_REGISTRATION = 0,
83 // Confirm that Chrome is fully integrated with Windows (i.e. registered with
84 // Default Programs). These registrations can be in HKCU as of Windows 8.
85 // Note: Shell registration implies ProgId registration.
86 CONFIRM_SHELL_REGISTRATION,
87 // Same as CONFIRM_SHELL_REGISTRATION, but only look in HKLM (used when
88 // uninstalling to know whether elevation is required to clean up the
89 // registry).
90 CONFIRM_SHELL_REGISTRATION_IN_HKLM,
91 };
92
93 const wchar_t kReinstallCommand[] = L"ReinstallCommand";
94
95 // Returns the current (or installed) browser's ProgId (e.g.
96 // "ChromeHTML|suffix|").
97 // |suffix| can be the empty string.
GetBrowserProgId(const base::string16 & suffix)98 base::string16 GetBrowserProgId(const base::string16& suffix) {
99 base::string16 chrome_html(install_static::GetProgIdPrefix());
100 chrome_html.append(suffix);
101
102 // ProgIds cannot be longer than 39 characters.
103 // Ref: http://msdn.microsoft.com/en-us/library/aa911706.aspx.
104 // Make all new registrations comply with this requirement (existing
105 // registrations must be preserved).
106 base::string16 new_style_suffix;
107 if (ShellUtil::GetUserSpecificRegistrySuffix(&new_style_suffix) &&
108 suffix == new_style_suffix && chrome_html.length() > 39) {
109 NOTREACHED();
110 chrome_html.erase(39);
111 }
112 return chrome_html;
113 }
114
115 // Returns the browser's application name. This application name will be
116 // suffixed as is appropriate for the current install. This is the name that is
117 // registered with Default Programs on Windows and that should thus be used to
118 // "make chrome default" and such.
GetApplicationName(const base::FilePath & chrome_exe)119 base::string16 GetApplicationName(const base::FilePath& chrome_exe) {
120 return install_static::GetBaseAppName().append(
121 ShellUtil::GetCurrentInstallationSuffix(chrome_exe));
122 }
123
124 // This class is used to initialize and cache a base 32 encoding of the md5 hash
125 // of this user's sid preceded by a dot.
126 // This is guaranteed to be unique on the machine and 27 characters long
127 // (including the '.').
128 // This is then meant to be used as a suffix on all registrations that may
129 // conflict with another user-level Chrome install.
130 class UserSpecificRegistrySuffix {
131 public:
132 // All the initialization is done in the constructor to be able to build the
133 // suffix in a thread-safe manner when used in conjunction with a
134 // LazyInstance.
135 UserSpecificRegistrySuffix();
136
137 // Sets |suffix| to the pre-computed suffix cached in this object.
138 // Returns true unless the initialization originally failed.
139 bool GetSuffix(base::string16* suffix);
140
141 private:
142 base::string16 suffix_;
143
144 DISALLOW_COPY_AND_ASSIGN(UserSpecificRegistrySuffix);
145 }; // class UserSpecificRegistrySuffix
146
UserSpecificRegistrySuffix()147 UserSpecificRegistrySuffix::UserSpecificRegistrySuffix() {
148 base::string16 user_sid;
149 if (!base::win::GetUserSidString(&user_sid)) {
150 NOTREACHED();
151 return;
152 }
153 static_assert(sizeof(base::MD5Digest) == 16, "size of MD5 not as expected");
154 base::MD5Digest md5_digest;
155 std::string user_sid_ascii(base::UTF16ToASCII(user_sid));
156 base::MD5Sum(user_sid_ascii.c_str(), user_sid_ascii.length(), &md5_digest);
157 std::string base32_md5 = base32::Base32Encode(
158 base::StringPiece(reinterpret_cast<char*>(md5_digest.a),
159 base::size(md5_digest.a)),
160 base32::Base32EncodePolicy::OMIT_PADDING);
161 // The value returned by the base32 algorithm above must never change.
162 DCHECK_EQ(base32_md5.length(), 26U);
163 suffix_.reserve(base32_md5.length() + 1);
164 suffix_.assign(1, L'.');
165 suffix_.append(base::ASCIIToUTF16(base32_md5));
166 }
167
GetSuffix(base::string16 * suffix)168 bool UserSpecificRegistrySuffix::GetSuffix(base::string16* suffix) {
169 if (suffix_.empty()) {
170 NOTREACHED();
171 return false;
172 }
173 suffix->assign(suffix_);
174 return true;
175 }
176
177 // Details about a Windows application, to be entered into the registry for the
178 // purpose of file associations.
179 struct ApplicationInfo {
ApplicationInfo__anon5717f4a50111::ApplicationInfo180 ApplicationInfo() : file_type_icon_index(0), application_icon_index(0) {}
181
182 // The ProgId used by Windows for file associations with this application.
183 // Must not be empty or start with a '.'.
184 base::string16 prog_id;
185 // The friendly name, and the path of the icon that will be used for files of
186 // these types when associated with this application by default. (They are NOT
187 // the name/icon that will represent the application under the Open With
188 // menu.)
189 base::string16 file_type_name;
190 base::FilePath file_type_icon_path;
191 int file_type_icon_index;
192 // The command to execute when opening a file via this association. It should
193 // contain "%1" (to tell Windows to pass the filename as an argument).
194 // TODO(mgiuca): |command_line| should be a base::CommandLine.
195 base::string16 command_line;
196 // The AppUserModelId used by Windows 8 for this application. Distinct from
197 // |prog_id|.
198 base::string16 app_id;
199
200 // User-visible details about this application. Any of these may be empty.
201 base::string16 application_name;
202 base::FilePath application_icon_path;
203 int application_icon_index;
204 base::string16 application_description;
205 base::string16 publisher_name;
206
207 // The CLSID for the application's DelegateExecute handler. May be empty.
208 base::string16 delegate_clsid;
209 };
210
211 // Returns the Windows browser client registration key for Chrome. For example:
212 // "Software\Clients\StartMenuInternet\Chromium[.user]". Strictly speaking, we
213 // should use the name of the executable (e.g., "chrome.exe"), but that ship has
214 // sailed. The cost of switching now is re-prompting users to make Chrome their
215 // default browser, which isn't polite. |suffix| is the user-specific
216 // registration suffix; see GetUserSpecificDefaultBrowserSuffix in shell_util.h
217 // for details.
GetBrowserClientKey(const base::string16 & suffix)218 base::string16 GetBrowserClientKey(const base::string16& suffix) {
219 DCHECK(suffix.empty() || suffix[0] == L'.');
220 return base::string16(ShellUtil::kRegStartMenuInternet)
221 .append(1, L'\\')
222 .append(install_static::GetBaseAppName())
223 .append(suffix);
224 }
225
226 // Returns the Windows Default Programs capabilities key for Chrome. For
227 // example:
228 // "Software\Clients\StartMenuInternet\Chromium[.user]\Capabilities".
GetCapabilitiesKey(const base::string16 & suffix)229 base::string16 GetCapabilitiesKey(const base::string16& suffix) {
230 return GetBrowserClientKey(suffix).append(L"\\Capabilities");
231 }
232
233 // DelegateExecute ProgId. Needed for Chrome Metro in Windows 8. This is only
234 // needed for registering a web browser, not for general associations.
GetChromeDelegateExecuteEntries(const base::FilePath & chrome_exe,const ApplicationInfo & app_info)235 std::vector<std::unique_ptr<RegistryEntry>> GetChromeDelegateExecuteEntries(
236 const base::FilePath& chrome_exe,
237 const ApplicationInfo& app_info) {
238 std::vector<std::unique_ptr<RegistryEntry>> entries;
239
240 base::string16 app_id_shell_key(ShellUtil::kRegClasses);
241 app_id_shell_key.push_back(base::FilePath::kSeparators[0]);
242 app_id_shell_key.append(app_info.app_id);
243 app_id_shell_key.append(ShellUtil::kRegExePath);
244 app_id_shell_key.append(ShellUtil::kRegShellPath);
245
246 // <root hkey>\Software\Classes\<app_id>\.exe\shell @=open
247 entries.push_back(std::make_unique<RegistryEntry>(app_id_shell_key,
248 ShellUtil::kRegVerbOpen));
249
250 // The command to execute when opening this application via the Metro UI.
251 const base::string16 delegate_command(
252 ShellUtil::GetChromeDelegateCommand(chrome_exe));
253
254 // Each of Chrome's shortcuts has an appid; which, as of Windows 8, is
255 // registered to handle some verbs. This registration has the side-effect
256 // that these verbs now show up in the shortcut's context menu. We
257 // mitigate this side-effect by making the context menu entries
258 // user readable/localized strings. See relevant MSDN article:
259 // http://msdn.microsoft.com/en-US/library/windows/desktop/cc144171.aspx
260 static const struct {
261 const wchar_t* verb;
262 int name_id;
263 } verbs[] = {
264 {ShellUtil::kRegVerbOpen, -1},
265 {ShellUtil::kRegVerbOpenNewWindow, IDS_SHORTCUT_NEW_WINDOW_BASE},
266 };
267 for (const auto& verb_and_id : verbs) {
268 base::string16 sub_path(app_id_shell_key);
269 sub_path.push_back(base::FilePath::kSeparators[0]);
270 sub_path.append(verb_and_id.verb);
271
272 // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb>
273 if (verb_and_id.name_id != -1) {
274 // TODO(grt): http://crbug.com/75152 Write a reference to a localized
275 // resource.
276 const base::string16 verb_name(
277 installer::GetLocalizedString(verb_and_id.name_id));
278 entries.push_back(
279 std::make_unique<RegistryEntry>(sub_path, verb_name.c_str()));
280 }
281 entries.push_back(std::make_unique<RegistryEntry>(sub_path, L"CommandId",
282 L"Browser.Launch"));
283
284 sub_path.push_back(base::FilePath::kSeparators[0]);
285 sub_path.append(ShellUtil::kRegCommand);
286
287 // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb>\command
288 entries.push_back(
289 std::make_unique<RegistryEntry>(sub_path, delegate_command));
290 entries.push_back(std::make_unique<RegistryEntry>(
291 sub_path, ShellUtil::kRegDelegateExecute, app_info.delegate_clsid));
292 }
293
294 return entries;
295 }
296
297 // Gets the registry entries to register an application in the Windows registry.
298 // |app_info| provides all of the information needed.
GetProgIdEntries(const ApplicationInfo & app_info,std::vector<std::unique_ptr<RegistryEntry>> * entries)299 void GetProgIdEntries(const ApplicationInfo& app_info,
300 std::vector<std::unique_ptr<RegistryEntry>>* entries) {
301 // Basic sanity checks.
302 DCHECK(!app_info.prog_id.empty());
303 DCHECK_NE(L'.', app_info.prog_id[0]);
304
305 // File association ProgId
306 base::string16 prog_id_path(ShellUtil::kRegClasses);
307 prog_id_path.push_back(base::FilePath::kSeparators[0]);
308 prog_id_path.append(app_info.prog_id);
309 entries->push_back(
310 std::make_unique<RegistryEntry>(prog_id_path, app_info.file_type_name));
311 entries->push_back(std::make_unique<RegistryEntry>(
312 prog_id_path + ShellUtil::kRegDefaultIcon,
313 ShellUtil::FormatIconLocation(app_info.file_type_icon_path,
314 app_info.file_type_icon_index)));
315 entries->push_back(std::make_unique<RegistryEntry>(
316 prog_id_path + ShellUtil::kRegShellOpen, app_info.command_line));
317 if (!app_info.delegate_clsid.empty()) {
318 entries->push_back(std::make_unique<RegistryEntry>(
319 prog_id_path + ShellUtil::kRegShellOpen, ShellUtil::kRegDelegateExecute,
320 app_info.delegate_clsid));
321 // TODO(scottmg): Simplify after Metro removal. https://crbug.com/558054.
322 entries->back()->set_removal_flag(RegistryEntry::RemovalFlag::VALUE);
323 }
324
325 // The following entries are required as of Windows 8, but do not
326 // depend on the DelegateExecute verb handler being set.
327 if (base::win::GetVersion() >= base::win::Version::WIN8) {
328 if (!app_info.app_id.empty()) {
329 entries->push_back(std::make_unique<RegistryEntry>(
330 prog_id_path, ShellUtil::kRegAppUserModelId, app_info.app_id));
331 }
332 }
333
334 // Add \Software\Classes\<prog_id>\Application entries
335 base::string16 application_path(prog_id_path + ShellUtil::kRegApplication);
336 if (!app_info.app_id.empty()) {
337 entries->push_back(std::make_unique<RegistryEntry>(
338 application_path, ShellUtil::kRegAppUserModelId, app_info.app_id));
339 }
340 if (!app_info.application_icon_path.empty()) {
341 entries->push_back(std::make_unique<RegistryEntry>(
342 application_path, ShellUtil::kRegApplicationIcon,
343 ShellUtil::FormatIconLocation(app_info.application_icon_path,
344 app_info.application_icon_index)));
345 }
346 if (!app_info.application_name.empty()) {
347 entries->push_back(std::make_unique<RegistryEntry>(
348 application_path, ShellUtil::kRegApplicationName,
349 app_info.application_name));
350 }
351 if (!app_info.application_description.empty()) {
352 entries->push_back(std::make_unique<RegistryEntry>(
353 application_path, ShellUtil::kRegApplicationDescription,
354 app_info.application_description));
355 }
356 if (!app_info.publisher_name.empty()) {
357 entries->push_back(std::make_unique<RegistryEntry>(
358 application_path, ShellUtil::kRegApplicationCompany,
359 app_info.publisher_name));
360 }
361 }
362
363 // This method returns a list of all the registry entries that are needed to
364 // register this installation's ProgId and AppId. These entries need to be
365 // registered in HKLM prior to Win8.
GetChromeProgIdEntries(const base::FilePath & chrome_exe,const base::string16 & suffix,std::vector<std::unique_ptr<RegistryEntry>> * entries)366 void GetChromeProgIdEntries(
367 const base::FilePath& chrome_exe,
368 const base::string16& suffix,
369 std::vector<std::unique_ptr<RegistryEntry>>* entries) {
370 int chrome_icon_index = install_static::GetIconResourceIndex();
371
372 ApplicationInfo app_info;
373 app_info.prog_id = GetBrowserProgId(suffix);
374 app_info.file_type_name = install_static::GetProgIdDescription();
375 // File types associated with Chrome are just given the Chrome icon.
376 app_info.file_type_icon_path = chrome_exe;
377 app_info.file_type_icon_index = chrome_icon_index;
378 app_info.command_line = ShellUtil::GetChromeShellOpenCmd(chrome_exe);
379 // For user-level installs: entries for the app id will be in HKCU; thus we
380 // do not need a suffix on those entries.
381 app_info.app_id =
382 ShellUtil::GetBrowserModelId(InstallUtil::IsPerUserInstall());
383
384 // TODO(grt): http://crbug.com/75152 Write a reference to a localized
385 // resource for name, description, and company.
386 app_info.application_name = InstallUtil::GetDisplayName();
387 app_info.application_icon_path = chrome_exe;
388 app_info.application_icon_index = chrome_icon_index;
389 app_info.application_description = InstallUtil::GetAppDescription();
390 app_info.publisher_name = InstallUtil::GetPublisherName();
391 app_info.delegate_clsid = install_static::GetLegacyCommandExecuteImplClsid();
392
393 GetProgIdEntries(app_info, entries);
394
395 if (!app_info.delegate_clsid.empty()) {
396 auto delegate_execute_entries =
397 GetChromeDelegateExecuteEntries(chrome_exe, app_info);
398 // Remove the keys (not only their values) so that Windows will continue
399 // to launch Chrome without a pesky association error.
400 // TODO(scottmg): Simplify after Metro removal. https://crbug.com/558054.
401 for (const auto& entry : delegate_execute_entries)
402 entry->set_removal_flag(RegistryEntry::RemovalFlag::KEY);
403 // Move |delegate_execute_entries| to |entries|.
404 std::move(delegate_execute_entries.begin(), delegate_execute_entries.end(),
405 std::back_inserter(*entries));
406 }
407 }
408
409 // This method returns a list of the registry entries needed to declare a
410 // capability of handling a protocol on Windows.
GetProtocolCapabilityEntries(const base::string16 & suffix,const base::string16 & protocol,std::vector<std::unique_ptr<RegistryEntry>> * entries)411 void GetProtocolCapabilityEntries(
412 const base::string16& suffix,
413 const base::string16& protocol,
414 std::vector<std::unique_ptr<RegistryEntry>>* entries) {
415 entries->push_back(std::make_unique<RegistryEntry>(
416 GetCapabilitiesKey(suffix).append(L"\\URLAssociations"), protocol,
417 GetBrowserProgId(suffix)));
418 }
419
420 // This method returns a list of the registry entries required to register this
421 // installation in "RegisteredApplications" on Windows (to appear in Default
422 // Programs, StartMenuInternet, etc.). These entries need to be registered in
423 // HKLM prior to Win8. If |suffix| is not empty, these entries are guaranteed to
424 // be unique on this machine.
GetShellIntegrationEntries(const base::FilePath & chrome_exe,const base::string16 & suffix,std::vector<std::unique_ptr<RegistryEntry>> * entries)425 void GetShellIntegrationEntries(
426 const base::FilePath& chrome_exe,
427 const base::string16& suffix,
428 std::vector<std::unique_ptr<RegistryEntry>>* entries) {
429 const base::string16 icon_path(ShellUtil::FormatIconLocation(
430 chrome_exe, install_static::GetIconResourceIndex()));
431 const base::string16 quoted_exe_path(L"\"" + chrome_exe.value() + L"\"");
432
433 // Register for the Start Menu "Internet" link (pre-Win7).
434 const base::string16 start_menu_entry(GetBrowserClientKey(suffix));
435 // Register Chrome's display name.
436 // TODO(grt): http://crbug.com/75152 Also set LocalizedString; see
437 // http://msdn.microsoft.com/en-us/library/windows/desktop/cc144109(v=VS.85).aspx#registering_the_display_name
438 entries->push_back(std::make_unique<RegistryEntry>(
439 start_menu_entry, InstallUtil::GetDisplayName()));
440 // Register the "open" verb for launching Chrome via the "Internet" link.
441 entries->push_back(std::make_unique<RegistryEntry>(
442 start_menu_entry + ShellUtil::kRegShellOpen, quoted_exe_path));
443 // Register Chrome's icon for the Start Menu "Internet" link.
444 entries->push_back(std::make_unique<RegistryEntry>(
445 start_menu_entry + ShellUtil::kRegDefaultIcon, icon_path));
446
447 // Register installation information.
448 base::string16 install_info(start_menu_entry + L"\\InstallInfo");
449 // Note: not using CommandLine since it has ambiguous rules for quoting
450 // strings.
451 entries->push_back(std::make_unique<RegistryEntry>(
452 install_info, kReinstallCommand,
453 quoted_exe_path + L" --" +
454 base::ASCIIToUTF16(switches::kMakeDefaultBrowser)));
455 entries->push_back(std::make_unique<RegistryEntry>(
456 install_info, L"HideIconsCommand",
457 quoted_exe_path + L" --" + base::ASCIIToUTF16(switches::kHideIcons)));
458 entries->push_back(std::make_unique<RegistryEntry>(
459 install_info, L"ShowIconsCommand",
460 quoted_exe_path + L" --" + base::ASCIIToUTF16(switches::kShowIcons)));
461 entries->push_back(
462 std::make_unique<RegistryEntry>(install_info, L"IconsVisible", 1));
463
464 // Register with Default Programs.
465 const base::string16 reg_app_name(
466 install_static::GetBaseAppName().append(suffix));
467 // Tell Windows where to find Chrome's Default Programs info.
468 const base::string16 capabilities(GetCapabilitiesKey(suffix));
469 entries->push_back(std::make_unique<RegistryEntry>(
470 ShellUtil::kRegRegisteredApplications, reg_app_name, capabilities));
471 // Write out Chrome's Default Programs info.
472 // TODO(grt): http://crbug.com/75152 Write a reference to a localized
473 // resource rather than this.
474 entries->push_back(std::make_unique<RegistryEntry>(
475 capabilities, ShellUtil::kRegApplicationDescription,
476 InstallUtil::GetLongAppDescription()));
477 entries->push_back(std::make_unique<RegistryEntry>(
478 capabilities, ShellUtil::kRegApplicationIcon, icon_path));
479 entries->push_back(std::make_unique<RegistryEntry>(
480 capabilities, ShellUtil::kRegApplicationName,
481 InstallUtil::GetDisplayName()));
482
483 entries->push_back(std::make_unique<RegistryEntry>(
484 capabilities + L"\\Startmenu", L"StartMenuInternet", reg_app_name));
485
486 const base::string16 html_prog_id(GetBrowserProgId(suffix));
487 for (int i = 0; ShellUtil::kPotentialFileAssociations[i] != nullptr; i++) {
488 entries->push_back(std::make_unique<RegistryEntry>(
489 capabilities + L"\\FileAssociations",
490 ShellUtil::kPotentialFileAssociations[i], html_prog_id));
491 }
492 for (int i = 0; ShellUtil::kPotentialProtocolAssociations[i] != nullptr;
493 i++) {
494 entries->push_back(std::make_unique<RegistryEntry>(
495 capabilities + L"\\URLAssociations",
496 ShellUtil::kPotentialProtocolAssociations[i], html_prog_id));
497 }
498 }
499
500 // Gets the registry entries to register an application as a handler for a
501 // particular file extension. |prog_id| is the ProgId used by Windows for the
502 // application. |ext| is the file extension, which must begin with a '.'.
GetAppExtRegistrationEntries(const base::string16 & prog_id,const base::string16 & ext,std::vector<std::unique_ptr<RegistryEntry>> * entries)503 void GetAppExtRegistrationEntries(
504 const base::string16& prog_id,
505 const base::string16& ext,
506 std::vector<std::unique_ptr<RegistryEntry>>* entries) {
507 // In HKEY_CURRENT_USER\Software\Classes\EXT\OpenWithProgids, create an
508 // empty value with this class's ProgId.
509 base::string16 key_name(ShellUtil::kRegClasses);
510 key_name.push_back(base::FilePath::kSeparators[0]);
511 key_name.append(ext);
512 key_name.push_back(base::FilePath::kSeparators[0]);
513 key_name.append(ShellUtil::kRegOpenWithProgids);
514 entries->push_back(
515 std::make_unique<RegistryEntry>(key_name, prog_id, base::string16()));
516 }
517
518 // This method returns a list of the registry entries required for this
519 // installation to be registered in the Windows shell.
520 // In particular:
521 // - App Paths
522 // http://msdn.microsoft.com/en-us/library/windows/desktop/ee872121
523 // - File Associations
524 // http://msdn.microsoft.com/en-us/library/bb166549
525 // These entries need to be registered in HKLM prior to Win8.
GetChromeAppRegistrationEntries(const base::FilePath & chrome_exe,const base::string16 & suffix,std::vector<std::unique_ptr<RegistryEntry>> * entries)526 void GetChromeAppRegistrationEntries(
527 const base::FilePath& chrome_exe,
528 const base::string16& suffix,
529 std::vector<std::unique_ptr<RegistryEntry>>* entries) {
530 base::string16 app_path_key(ShellUtil::kAppPathsRegistryKey);
531 app_path_key.push_back(base::FilePath::kSeparators[0]);
532 app_path_key.append(chrome_exe.BaseName().value());
533 entries->push_back(
534 std::make_unique<RegistryEntry>(app_path_key, chrome_exe.value()));
535 entries->push_back(std::make_unique<RegistryEntry>(
536 app_path_key, ShellUtil::kAppPathsRegistryPathName,
537 chrome_exe.DirName().value()));
538
539 const base::string16 html_prog_id(GetBrowserProgId(suffix));
540 for (int i = 0; ShellUtil::kPotentialFileAssociations[i] != nullptr; i++) {
541 GetAppExtRegistrationEntries(
542 html_prog_id, ShellUtil::kPotentialFileAssociations[i], entries);
543 }
544 }
545
546 // Gets the registry entries to register an application as the default handler
547 // for a particular file extension. |prog_id| is the ProgId used by Windows for
548 // the application. |ext| is the file extension, which must begin with a '.'. If
549 // |overwrite_existing|, always sets the default handler; otherwise only sets if
550 // there is no existing default.
551 //
552 // This has no effect on Windows 8. Windows 8 ignores the default and lets the
553 // user choose. If there is only one handler for a file, it will automatically
554 // become the default. Otherwise, the first time the user opens a file, they are
555 // presented with the dialog to set the default handler. (This is roughly
556 // equivalent to being called with |overwrite_existing| false.)
GetAppDefaultRegistrationEntries(const base::string16 & prog_id,const base::string16 & ext,bool overwrite_existing,std::vector<std::unique_ptr<RegistryEntry>> * entries)557 void GetAppDefaultRegistrationEntries(
558 const base::string16& prog_id,
559 const base::string16& ext,
560 bool overwrite_existing,
561 std::vector<std::unique_ptr<RegistryEntry>>* entries) {
562 // Set the default value of HKEY_CURRENT_USER\Software\Classes\EXT to this
563 // class's name.
564 base::string16 key_name(ShellUtil::kRegClasses);
565 key_name.push_back(base::FilePath::kSeparators[0]);
566 key_name.append(ext);
567 auto default_association = std::make_unique<RegistryEntry>(key_name, prog_id);
568 if (overwrite_existing ||
569 !default_association->KeyExistsInRegistry(RegistryEntry::LOOK_IN_HKCU)) {
570 entries->push_back(std::move(default_association));
571 }
572 }
573
574 // This method returns a list of all the user level registry entries that are
575 // needed to make Chromium the default handler for a protocol on XP.
GetXPStyleUserProtocolEntries(const base::string16 & protocol,const base::string16 & chrome_icon,const base::string16 & chrome_open,std::vector<std::unique_ptr<RegistryEntry>> * entries)576 void GetXPStyleUserProtocolEntries(
577 const base::string16& protocol,
578 const base::string16& chrome_icon,
579 const base::string16& chrome_open,
580 std::vector<std::unique_ptr<RegistryEntry>>* entries) {
581 // Protocols associations.
582 base::string16 url_key(ShellUtil::kRegClasses);
583 url_key.push_back(base::FilePath::kSeparators[0]);
584 url_key.append(protocol);
585
586 // This registry value tells Windows that this 'class' is a URL scheme
587 // so IE, explorer and other apps will route it to our handler.
588 // <root hkey>\Software\Classes\<protocol>\URL Protocol
589 entries->push_back(std::make_unique<RegistryEntry>(
590 url_key, ShellUtil::kRegUrlProtocol, base::string16()));
591
592 // <root hkey>\Software\Classes\<protocol>\DefaultIcon
593 base::string16 icon_key = url_key + ShellUtil::kRegDefaultIcon;
594 entries->push_back(std::make_unique<RegistryEntry>(icon_key, chrome_icon));
595
596 // <root hkey>\Software\Classes\<protocol>\shell\open\command
597 base::string16 shell_key = url_key + ShellUtil::kRegShellOpen;
598 entries->push_back(std::make_unique<RegistryEntry>(shell_key, chrome_open));
599
600 // <root hkey>\Software\Classes\<protocol>\shell\open\ddeexec
601 base::string16 dde_key = url_key + L"\\shell\\open\\ddeexec";
602 entries->push_back(
603 std::make_unique<RegistryEntry>(dde_key, base::string16()));
604
605 // <root hkey>\Software\Classes\<protocol>\shell\@
606 base::string16 protocol_shell_key = url_key + ShellUtil::kRegShellPath;
607 entries->push_back(
608 std::make_unique<RegistryEntry>(protocol_shell_key, L"open"));
609 }
610
611 // This method returns a list of all the user level registry entries that are
612 // needed to make Chromium default browser on XP. Some of these entries are
613 // irrelevant in recent versions of Windows, but we register them anyways as
614 // some legacy apps are hardcoded to lookup those values.
GetXPStyleDefaultBrowserUserEntries(const base::FilePath & chrome_exe,const base::string16 & suffix,std::vector<std::unique_ptr<RegistryEntry>> * entries)615 void GetXPStyleDefaultBrowserUserEntries(
616 const base::FilePath& chrome_exe,
617 const base::string16& suffix,
618 std::vector<std::unique_ptr<RegistryEntry>>* entries) {
619 // File extension associations.
620 base::string16 html_prog_id(GetBrowserProgId(suffix));
621 for (int i = 0; ShellUtil::kDefaultFileAssociations[i] != nullptr; i++) {
622 GetAppDefaultRegistrationEntries(
623 html_prog_id, ShellUtil::kDefaultFileAssociations[i], true, entries);
624 }
625
626 // Protocols associations.
627 base::string16 chrome_open = ShellUtil::GetChromeShellOpenCmd(chrome_exe);
628 base::string16 chrome_icon = ShellUtil::FormatIconLocation(
629 chrome_exe, install_static::GetIconResourceIndex());
630 for (int i = 0; ShellUtil::kBrowserProtocolAssociations[i] != nullptr; i++) {
631 GetXPStyleUserProtocolEntries(ShellUtil::kBrowserProtocolAssociations[i],
632 chrome_icon, chrome_open, entries);
633 }
634
635 // start->Internet shortcut.
636 base::string16 start_menu(ShellUtil::kRegStartMenuInternet);
637 base::string16 app_name = install_static::GetBaseAppName().append(suffix);
638 entries->push_back(std::make_unique<RegistryEntry>(start_menu, app_name));
639 }
640
641 // Checks that all |entries| are present on this computer (or absent if their
642 // |removal_flag_| is set). |look_for_in| is passed to
643 // RegistryEntry::ExistsInRegistry(). Documentation for it can be found there.
AreEntriesAsDesired(const std::vector<std::unique_ptr<RegistryEntry>> & entries,uint32_t look_for_in)644 bool AreEntriesAsDesired(
645 const std::vector<std::unique_ptr<RegistryEntry>>& entries,
646 uint32_t look_for_in) {
647 for (const auto& entry : entries) {
648 if (entry->ExistsInRegistry(look_for_in) != !entry->IsFlaggedForRemoval())
649 return false;
650 }
651 return true;
652 }
653
654 // Checks that all required registry entries for Chrome are already present on
655 // this computer (or absent if their |removal_flag_| is set).
656 // See RegistryEntry::ExistsInRegistry for the behavior of |look_for_in|.
657 // Note: between r133333 and r154145 we were registering parts of Chrome in HKCU
658 // and parts in HKLM for user-level installs; we now always register everything
659 // under a single registry root. Not doing so caused http://crbug.com/144910 for
660 // users who first-installed Chrome in that revision range (those users are
661 // still impacted by http://crbug.com/144910). This method will keep returning
662 // true for affected users (i.e. who have all the registrations, but over both
663 // registry roots).
IsChromeRegistered(const base::FilePath & chrome_exe,const base::string16 & suffix,uint32_t look_for_in)664 bool IsChromeRegistered(const base::FilePath& chrome_exe,
665 const base::string16& suffix,
666 uint32_t look_for_in) {
667 std::vector<std::unique_ptr<RegistryEntry>> entries;
668 GetChromeProgIdEntries(chrome_exe, suffix, &entries);
669 GetShellIntegrationEntries(chrome_exe, suffix, &entries);
670 GetChromeAppRegistrationEntries(chrome_exe, suffix, &entries);
671 return AreEntriesAsDesired(entries, look_for_in);
672 }
673
674 // This method checks if Chrome is already registered on the local machine
675 // for the requested protocol. It just checks the one value required for this.
676 // See RegistryEntry::ExistsInRegistry for the behavior of |look_for_in|.
IsChromeRegisteredForProtocol(const base::string16 & suffix,const base::string16 & protocol,uint32_t look_for_in)677 bool IsChromeRegisteredForProtocol(const base::string16& suffix,
678 const base::string16& protocol,
679 uint32_t look_for_in) {
680 std::vector<std::unique_ptr<RegistryEntry>> entries;
681 GetProtocolCapabilityEntries(suffix, protocol, &entries);
682 return AreEntriesAsDesired(entries, look_for_in);
683 }
684
685 // This method registers Chrome by launching an elevated setup.exe. That will
686 // show the user the standard elevation prompt. If the user accepts it the new
687 // process will make the necessary changes and return SUCCESS that we capture
688 // and return. If protocol is non-empty we will also register Chrome as being
689 // capable of handling the protocol. This is used for general browser
690 // registration on Windows 7 for per-user installs where setup.exe did not have
691 // permission to register Chrome during install. It may also be used on Windows
692 // 7 for system-level installs to register Chrome for a specific protocol.
ElevateAndRegisterChrome(const base::FilePath & chrome_exe,const base::string16 & suffix,const base::string16 & protocol)693 bool ElevateAndRegisterChrome(const base::FilePath& chrome_exe,
694 const base::string16& suffix,
695 const base::string16& protocol) {
696 // Check for setup.exe in the same directory as chrome.exe, as is the case
697 // when running out of a build output directory.
698 base::FilePath exe_path = chrome_exe.DirName().Append(installer::kSetupExe);
699
700 // Failing that, read the path to setup.exe from Chrome's ClientState key,
701 // which is the canonical location of the installer for all types of installs
702 // (see AddUninstallShortcutWorkItems).
703 const bool is_per_user = InstallUtil::IsPerUserInstall();
704 if (!base::PathExists(exe_path)) {
705 RegKey key(is_per_user ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE,
706 install_static::GetClientStateKeyPath().c_str(),
707 KEY_QUERY_VALUE | KEY_WOW64_32KEY);
708 base::string16 uninstall_string;
709 if (key.ReadValue(installer::kUninstallStringField, &uninstall_string) ==
710 ERROR_SUCCESS) {
711 exe_path = base::FilePath(uninstall_string);
712 }
713 }
714
715 if (base::PathExists(exe_path)) {
716 base::CommandLine cmd(exe_path);
717 InstallUtil::AppendModeAndChannelSwitches(&cmd);
718 if (!is_per_user)
719 cmd.AppendSwitch(installer::switches::kSystemLevel);
720 cmd.AppendSwitchPath(installer::switches::kRegisterChromeBrowser,
721 chrome_exe);
722 if (!suffix.empty()) {
723 cmd.AppendSwitchNative(installer::switches::kRegisterChromeBrowserSuffix,
724 suffix);
725 }
726
727 if (!protocol.empty()) {
728 cmd.AppendSwitchNative(installer::switches::kRegisterURLProtocol,
729 protocol);
730 }
731
732 DWORD ret_val = 0;
733 InstallUtil::ExecuteExeAsAdmin(cmd, &ret_val);
734 if (ret_val == 0)
735 return true;
736 }
737 return false;
738 }
739
740 // Returns the target used as a activate parameter when opening the settings
741 // pointing to the page that is the most relevant to a user trying to change the
742 // default handler for |protocol|.
GetTargetForDefaultAppsSettings(const wchar_t * protocol)743 base::string16 GetTargetForDefaultAppsSettings(const wchar_t* protocol) {
744 static const wchar_t kSystemSettingsDefaultAppsFormat[] =
745 L"SystemSettings_DefaultApps_%ls";
746
747 if (base::EqualsCaseInsensitiveASCII(protocol, L"http"))
748 return base::StringPrintf(kSystemSettingsDefaultAppsFormat, L"Browser");
749 if (base::EqualsCaseInsensitiveASCII(protocol, L"mailto"))
750 return base::StringPrintf(kSystemSettingsDefaultAppsFormat, L"Email");
751 return L"SettingsPageAppsDefaultsProtocolView";
752 }
753
754 // Launches the Windows 'settings' modern app with the 'default apps' view
755 // focused. This only works for Windows 8 and Windows 10. The appModelId
756 // looks arbitrary but it is the same in Win8 and Win10. There is no easy way to
757 // retrieve the appModelId from the registry.
LaunchDefaultAppsSettingsModernDialog(const wchar_t * protocol)758 bool LaunchDefaultAppsSettingsModernDialog(const wchar_t* protocol) {
759 DCHECK(protocol);
760 static const wchar_t kControlPanelAppModelId[] =
761 L"windows.immersivecontrolpanel_cw5n1h2txyewy"
762 L"!microsoft.windows.immersivecontrolpanel";
763
764 static constexpr base::Feature kHighlightProtocolInWindowsSettings{
765 "HighlightProtocolInWindowsSettings", base::FEATURE_ENABLED_BY_DEFAULT};
766
767 Microsoft::WRL::ComPtr<IApplicationActivationManager> activator;
768 HRESULT hr = ::CoCreateInstance(CLSID_ApplicationActivationManager, nullptr,
769 CLSCTX_ALL, IID_PPV_ARGS(&activator));
770 if (SUCCEEDED(hr)) {
771 DWORD pid = 0;
772 CoAllowSetForegroundWindow(activator.Get(), nullptr);
773 hr = activator->ActivateApplication(kControlPanelAppModelId,
774 L"page=SettingsPageAppsDefaults",
775 AO_NONE, &pid);
776 if (SUCCEEDED(hr) &&
777 base::FeatureList::IsEnabled(kHighlightProtocolInWindowsSettings)) {
778 hr = activator->ActivateApplication(
779 kControlPanelAppModelId,
780 base::StringPrintf(L"page=SettingsPageAppsDefaults&target=%ls",
781 GetTargetForDefaultAppsSettings(protocol).c_str())
782 .c_str(),
783 AO_NONE, &pid);
784 }
785 if (SUCCEEDED(hr))
786 return true;
787 base::UmaHistogramSparse("DefaultBrowser.ActivateSettings.ErrorHresult",
788 hr);
789 }
790 return false;
791 }
792
793 // Launches the Windows 7 and Windows 8 dialog for picking the application to
794 // handle the given protocol. Most importantly, this is used to set the default
795 // handler for http (and, implicitly with it, https). In that case it is also
796 // known as the 'how do you want to open webpages' dialog.
797 // It is required that Chrome be already *registered* for the given protocol.
LaunchSelectDefaultProtocolHandlerDialog(const wchar_t * protocol)798 bool LaunchSelectDefaultProtocolHandlerDialog(const wchar_t* protocol) {
799 DCHECK(protocol);
800 OPENASINFO open_as_info = {};
801 open_as_info.pcszFile = protocol;
802 open_as_info.oaifInFlags =
803 OAIF_URL_PROTOCOL | OAIF_FORCE_REGISTRATION | OAIF_REGISTER_EXT;
804 HRESULT hr = SHOpenWithDialog(nullptr, &open_as_info);
805 DLOG_IF(WARNING, FAILED(hr)) << "Failed to set as default " << protocol
806 << " handler; hr=0x" << std::hex << hr;
807 if (FAILED(hr))
808 return false;
809 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);
810 return true;
811 }
812
813 // Returns true if |chrome_exe| has been registered with |suffix| for |mode|.
814 // |confirmation_level| is the level of verification desired as described in
815 // the RegistrationConfirmationLevel enum above.
816 // |suffix| can be the empty string (this is used to support old installs
817 // where we used to not suffix user-level installs if they were the first to
818 // request the non-suffixed registry entries on the machine).
819 // NOTE: This a quick check that only validates that a single registry entry
820 // points to |chrome_exe|. This should only be used at run-time to determine
821 // how Chrome is registered, not to know whether the registration is complete
822 // at install-time (IsChromeRegistered() can be used for that).
QuickIsChromeRegisteredForMode(const base::FilePath & chrome_exe,const base::string16 & suffix,const install_static::InstallConstants & mode,RegistrationConfirmationLevel confirmation_level)823 bool QuickIsChromeRegisteredForMode(
824 const base::FilePath& chrome_exe,
825 const base::string16& suffix,
826 const install_static::InstallConstants& mode,
827 RegistrationConfirmationLevel confirmation_level) {
828 // Get the appropriate key to look for based on the level desired.
829 base::string16 reg_key;
830 switch (confirmation_level) {
831 case CONFIRM_PROGID_REGISTRATION:
832 // Software\Classes\ChromeHTML|suffix|
833 reg_key = ShellUtil::kRegClasses;
834 reg_key.push_back(base::FilePath::kSeparators[0]);
835 reg_key.append(mode.prog_id_prefix);
836 reg_key.append(suffix);
837 break;
838 case CONFIRM_SHELL_REGISTRATION:
839 case CONFIRM_SHELL_REGISTRATION_IN_HKLM:
840 // Software\Clients\StartMenuInternet\Google Chrome|suffix|
841 reg_key = GetBrowserClientKey(suffix);
842 break;
843 default:
844 NOTREACHED();
845 break;
846 }
847 reg_key.append(ShellUtil::kRegShellOpen);
848
849 // ProgId registrations are allowed to reside in HKCU for user-level installs
850 // (and values there have priority over values in HKLM). The same is true for
851 // shell integration entries as of Windows 8.
852 if (confirmation_level == CONFIRM_PROGID_REGISTRATION ||
853 (confirmation_level == CONFIRM_SHELL_REGISTRATION &&
854 base::win::GetVersion() >= base::win::Version::WIN8)) {
855 const RegKey key_hkcu(HKEY_CURRENT_USER, reg_key.c_str(), KEY_QUERY_VALUE);
856 base::string16 hkcu_value;
857 // If |reg_key| is present in HKCU, assert that it points to |chrome_exe|.
858 // Otherwise, fall back on an HKLM lookup below.
859 if (key_hkcu.ReadValue(L"", &hkcu_value) == ERROR_SUCCESS)
860 return InstallUtil::ProgramCompare(chrome_exe).Evaluate(hkcu_value);
861 }
862
863 // Assert that |reg_key| points to |chrome_exe| in HKLM.
864 const RegKey key_hklm(HKEY_LOCAL_MACHINE, reg_key.c_str(), KEY_QUERY_VALUE);
865 base::string16 hklm_value;
866 if (key_hklm.ReadValue(L"", &hklm_value) == ERROR_SUCCESS)
867 return InstallUtil::ProgramCompare(chrome_exe).Evaluate(hklm_value);
868 return false;
869 }
870
871 // Returns the installation suffix for |mode| at the system or user level based
872 // on |system_install|.
GetInstallationSuffixForModeAtLevel(const install_static::InstallConstants & mode,bool system_install)873 base::string16 GetInstallationSuffixForModeAtLevel(
874 const install_static::InstallConstants& mode,
875 bool system_install) {
876 // Search based on the existing install location. If no existing install
877 // found, uses the default install location for the mode.
878 const base::FilePath chrome_exe =
879 installer::GetChromeInstallPath(system_install)
880 .Append(installer::kChromeExe);
881
882 // See the comment in ShellUtil::GetCurrentInstallationSuffix for details on
883 // what's going on here.
884 base::string16 tested_suffix;
885 if (!system_install &&
886 (!ShellUtil::GetUserSpecificRegistrySuffix(&tested_suffix) ||
887 !QuickIsChromeRegisteredForMode(chrome_exe, tested_suffix, mode,
888 CONFIRM_PROGID_REGISTRATION)) &&
889 (!ShellUtil::GetOldUserSpecificRegistrySuffix(&tested_suffix) ||
890 !QuickIsChromeRegisteredForMode(chrome_exe, tested_suffix, mode,
891 CONFIRM_PROGID_REGISTRATION)) &&
892 !QuickIsChromeRegisteredForMode(chrome_exe, tested_suffix.erase(), mode,
893 CONFIRM_PROGID_REGISTRATION)) {
894 // If Chrome is not registered under any of the possible suffixes (e.g.
895 // tests, Canary, etc.): use the new-style suffix at run-time.
896 if (!ShellUtil::GetUserSpecificRegistrySuffix(&tested_suffix))
897 NOTREACHED();
898 }
899 return tested_suffix;
900 }
901
902 // Returns |mode|'s application name at the system or user level based on
903 // |system_install|. This application name will be suffixed as is appropriate
904 // for the install. This is the name that is registered with Default Programs on
905 // Windows and that should thus be used to "make chrome default" and such.
GetApplicationNameForModeAtLevel(const install_static::InstallConstants & mode,bool system_install)906 base::string16 GetApplicationNameForModeAtLevel(
907 const install_static::InstallConstants& mode,
908 bool system_install) {
909 return base::string16(mode.base_app_name)
910 .append(GetInstallationSuffixForModeAtLevel(mode, system_install));
911 }
912
913 // Returns true if the current install's |chrome_exe| has been registered with
914 // |suffix|.
915 // |confirmation_level| is the level of verification desired as described in
916 // the RegistrationConfirmationLevel enum above.
917 // |suffix| can be the empty string (this is used to support old installs
918 // where we used to not suffix user-level installs if they were the first to
919 // request the non-suffixed registry entries on the machine).
920 // NOTE: This a quick check that only validates that a single registry entry
921 // points to |chrome_exe|. This should only be used at run-time to determine
922 // how Chrome is registered, not to know whether the registration is complete
923 // at install-time (IsChromeRegistered() can be used for that).
QuickIsChromeRegistered(const base::FilePath & chrome_exe,const base::string16 & suffix,RegistrationConfirmationLevel confirmation_level)924 bool QuickIsChromeRegistered(const base::FilePath& chrome_exe,
925 const base::string16& suffix,
926 RegistrationConfirmationLevel confirmation_level) {
927 return QuickIsChromeRegisteredForMode(
928 chrome_exe, suffix, install_static::InstallDetails::Get().mode(),
929 confirmation_level);
930 }
931
932 // Sets |suffix| to a 27 character string that is specific to this user on this
933 // machine (on user-level installs only).
934 // To support old-style user-level installs however, |suffix| is cleared if the
935 // user currently owns the non-suffixed HKLM registrations.
936 // |suffix| can also be set to the user's username if the current install is
937 // suffixed as per the old-style registrations.
938 // |suffix| is cleared on system-level installs.
939 // |suffix| should then be appended to all Chrome properties that may conflict
940 // with other Chrome user-level installs.
941 // Returns true unless one of the underlying calls fails.
GetInstallationSpecificSuffix(const base::FilePath & chrome_exe,base::string16 * suffix)942 bool GetInstallationSpecificSuffix(const base::FilePath& chrome_exe,
943 base::string16* suffix) {
944 if (!InstallUtil::IsPerUserInstall() ||
945 QuickIsChromeRegistered(chrome_exe, base::string16(),
946 CONFIRM_SHELL_REGISTRATION)) {
947 // No suffix on system-level installs and user-level installs already
948 // registered with no suffix.
949 suffix->clear();
950 return true;
951 }
952
953 // Get the old suffix for the check below.
954 if (!ShellUtil::GetOldUserSpecificRegistrySuffix(suffix)) {
955 NOTREACHED();
956 return false;
957 }
958 if (QuickIsChromeRegistered(chrome_exe, *suffix,
959 CONFIRM_SHELL_REGISTRATION)) {
960 // Username suffix for installs that are suffixed as per the old-style.
961 return true;
962 }
963
964 return ShellUtil::GetUserSpecificRegistrySuffix(suffix);
965 }
966
967 // Returns the root registry key (HKLM or HKCU) under which registrations must
968 // be placed for this install. As of Windows 8 everything can go in HKCU for
969 // per-user installs.
DetermineRegistrationRoot(bool is_per_user)970 HKEY DetermineRegistrationRoot(bool is_per_user) {
971 return is_per_user && base::win::GetVersion() >= base::win::Version::WIN8
972 ? HKEY_CURRENT_USER
973 : HKEY_LOCAL_MACHINE;
974 }
975
976 // Associates Chrome with supported protocols and file associations. This should
977 // not be required on Vista+ but since some applications still read
978 // Software\Classes\http key directly, we have to do this on Vista+ as well.
RegisterChromeAsDefaultXPStyle(int shell_change,const base::FilePath & chrome_exe)979 bool RegisterChromeAsDefaultXPStyle(int shell_change,
980 const base::FilePath& chrome_exe) {
981 bool ret = true;
982 std::vector<std::unique_ptr<RegistryEntry>> entries;
983 GetXPStyleDefaultBrowserUserEntries(
984 chrome_exe, ShellUtil::GetCurrentInstallationSuffix(chrome_exe),
985 &entries);
986
987 // Change the default browser for current user.
988 if ((shell_change & ShellUtil::CURRENT_USER) &&
989 !ShellUtil::AddRegistryEntries(HKEY_CURRENT_USER, entries)) {
990 ret = false;
991 LOG(ERROR) << "Could not make Chrome default browser (XP/current user).";
992 }
993
994 // Chrome as default browser at system level.
995 if ((shell_change & ShellUtil::SYSTEM_LEVEL) &&
996 !ShellUtil::AddRegistryEntries(HKEY_LOCAL_MACHINE, entries)) {
997 ret = false;
998 LOG(ERROR) << "Could not make Chrome default browser (XP/system level).";
999 }
1000
1001 return ret;
1002 }
1003
1004 // Associates Chrome with |protocol| in the registry. This should not be
1005 // required on Vista+ but since some applications still read these registry
1006 // keys directly, we have to do this on Vista+ as well.
1007 // See http://msdn.microsoft.com/library/aa767914.aspx for more details.
RegisterChromeAsDefaultProtocolClientXPStyle(const base::FilePath & chrome_exe,const base::string16 & protocol)1008 bool RegisterChromeAsDefaultProtocolClientXPStyle(
1009 const base::FilePath& chrome_exe,
1010 const base::string16& protocol) {
1011 std::vector<std::unique_ptr<RegistryEntry>> entries;
1012 const base::string16 chrome_open(
1013 ShellUtil::GetChromeShellOpenCmd(chrome_exe));
1014 const base::string16 chrome_icon(ShellUtil::FormatIconLocation(
1015 chrome_exe, install_static::GetIconResourceIndex()));
1016 GetXPStyleUserProtocolEntries(protocol, chrome_icon, chrome_open, &entries);
1017 // Change the default protocol handler for current user.
1018 if (!ShellUtil::AddRegistryEntries(HKEY_CURRENT_USER, entries)) {
1019 LOG(ERROR) << "Could not make Chrome default protocol client (XP).";
1020 return false;
1021 }
1022
1023 return true;
1024 }
1025
1026 // Returns |properties.shortcut_name| if the property is set, otherwise it
1027 // returns InstallUtil::GetShortcutName(). In any case, it makes sure the return
1028 // value is suffixed with ".lnk".
ExtractShortcutNameFromProperties(const ShellUtil::ShortcutProperties & properties)1029 base::string16 ExtractShortcutNameFromProperties(
1030 const ShellUtil::ShortcutProperties& properties) {
1031 base::string16 shortcut_name = properties.has_shortcut_name()
1032 ? properties.shortcut_name
1033 : InstallUtil::GetShortcutName();
1034
1035 if (!base::EndsWith(shortcut_name, installer::kLnkExt,
1036 base::CompareCase::INSENSITIVE_ASCII))
1037 shortcut_name.append(installer::kLnkExt);
1038
1039 return shortcut_name;
1040 }
1041
1042 // Converts ShellUtil::ShortcutOperation to the best-matching value in
1043 // base::win::ShortcutOperation.
TranslateShortcutOperation(ShellUtil::ShortcutOperation operation)1044 base::win::ShortcutOperation TranslateShortcutOperation(
1045 ShellUtil::ShortcutOperation operation) {
1046 switch (operation) {
1047 case ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS: // Falls through.
1048 case ShellUtil::SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL:
1049 return base::win::SHORTCUT_CREATE_ALWAYS;
1050
1051 case ShellUtil::SHELL_SHORTCUT_UPDATE_EXISTING:
1052 return base::win::SHORTCUT_UPDATE_EXISTING;
1053
1054 case ShellUtil::SHELL_SHORTCUT_REPLACE_EXISTING:
1055 return base::win::SHORTCUT_REPLACE_EXISTING;
1056
1057 default:
1058 NOTREACHED();
1059 return base::win::SHORTCUT_REPLACE_EXISTING;
1060 }
1061 }
1062
1063 // Returns a base::win::ShortcutProperties struct containing the properties
1064 // to set on the shortcut based on the provided ShellUtil::ShortcutProperties.
TranslateShortcutProperties(const ShellUtil::ShortcutProperties & properties)1065 base::win::ShortcutProperties TranslateShortcutProperties(
1066 const ShellUtil::ShortcutProperties& properties) {
1067 base::win::ShortcutProperties shortcut_properties;
1068
1069 if (properties.has_target()) {
1070 shortcut_properties.set_target(properties.target);
1071 DCHECK(!properties.target.DirName().empty());
1072 shortcut_properties.set_working_dir(properties.target.DirName());
1073 }
1074
1075 if (properties.has_arguments())
1076 shortcut_properties.set_arguments(properties.arguments);
1077
1078 if (properties.has_description())
1079 shortcut_properties.set_description(properties.description);
1080
1081 if (properties.has_icon())
1082 shortcut_properties.set_icon(properties.icon, properties.icon_index);
1083
1084 if (properties.has_app_id())
1085 shortcut_properties.set_app_id(properties.app_id);
1086
1087 if (properties.has_toast_activator_clsid()) {
1088 shortcut_properties.set_toast_activator_clsid(
1089 properties.toast_activator_clsid);
1090 }
1091
1092 return shortcut_properties;
1093 }
1094
1095 // Cleans up an old verb (run) we used to register in
1096 // <root>\Software\Classes\Chrome<.suffix>\.exe\shell\run on Windows 8.
RemoveRunVerbOnWindows8()1097 void RemoveRunVerbOnWindows8() {
1098 if (base::win::GetVersion() >= base::win::Version::WIN8) {
1099 bool is_per_user_install = InstallUtil::IsPerUserInstall();
1100 HKEY root_key = DetermineRegistrationRoot(is_per_user_install);
1101 // There's no need to rollback, so forgo the usual work item lists and just
1102 // remove the key from the registry.
1103 base::string16 run_verb_key(ShellUtil::kRegClasses);
1104 run_verb_key.push_back(base::FilePath::kSeparators[0]);
1105 run_verb_key.append(ShellUtil::GetBrowserModelId(is_per_user_install));
1106 run_verb_key.append(ShellUtil::kRegExePath);
1107 run_verb_key.append(ShellUtil::kRegShellPath);
1108 run_verb_key.push_back(base::FilePath::kSeparators[0]);
1109 run_verb_key.append(ShellUtil::kRegVerbRun);
1110 InstallUtil::DeleteRegistryKey(root_key, run_verb_key,
1111 WorkItem::kWow64Default);
1112 }
1113 }
1114
1115 // Probe using IApplicationAssociationRegistration::QueryCurrentDefault
1116 // (Windows 8); see ProbeProtocolHandlers. This mechanism is not suitable for
1117 // use on previous versions of Windows despite the presence of
1118 // QueryCurrentDefault on them since versions of Windows prior to Windows 8
1119 // did not perform validation on the ProgID registered as the current default.
1120 // As a result, stale ProgIDs could be returned, leading to false positives.
ProbeCurrentDefaultHandlers(const base::FilePath & chrome_exe,const wchar_t * const * protocols,size_t num_protocols)1121 ShellUtil::DefaultState ProbeCurrentDefaultHandlers(
1122 const base::FilePath& chrome_exe,
1123 const wchar_t* const* protocols,
1124 size_t num_protocols) {
1125 Microsoft::WRL::ComPtr<IApplicationAssociationRegistration> registration;
1126 HRESULT hr =
1127 ::CoCreateInstance(CLSID_ApplicationAssociationRegistration, nullptr,
1128 CLSCTX_INPROC, IID_PPV_ARGS(®istration));
1129 if (FAILED(hr))
1130 return ShellUtil::UNKNOWN_DEFAULT;
1131
1132 // Get the ProgID for the current install mode.
1133 base::string16 prog_id(install_static::GetProgIdPrefix());
1134 prog_id += ShellUtil::GetCurrentInstallationSuffix(chrome_exe);
1135
1136 const int current_install_mode_index =
1137 install_static::InstallDetails::Get().install_mode_index();
1138 bool other_mode_is_default = false;
1139 for (size_t i = 0; i < num_protocols; ++i) {
1140 base::win::ScopedCoMem<wchar_t> current_app;
1141 hr = registration->QueryCurrentDefault(protocols[i], AT_URLPROTOCOL,
1142 AL_EFFECTIVE, ¤t_app);
1143 if (FAILED(hr))
1144 return ShellUtil::NOT_DEFAULT;
1145 if (prog_id.compare(current_app) == 0)
1146 continue;
1147
1148 // See if another mode is the default handler for this protocol.
1149 size_t current_app_len = std::char_traits<wchar_t>::length(current_app);
1150 const auto* it = std::find_if(
1151 &install_static::kInstallModes[0],
1152 &install_static::kInstallModes[install_static::NUM_INSTALL_MODES],
1153 [current_install_mode_index, ¤t_app,
1154 current_app_len](const install_static::InstallConstants& mode) {
1155 if (mode.index == current_install_mode_index)
1156 return false;
1157 const base::string16 mode_prog_id_prefix(mode.prog_id_prefix);
1158 // Does the current app either match this mode's ProgID or contain
1159 // this mode's ProgID as a prefix followed by the '.' separator for a
1160 // per-user install's suffix?
1161 return mode_prog_id_prefix.compare(current_app) == 0 ||
1162 (InstallUtil::IsPerUserInstall() &&
1163 current_app_len > mode_prog_id_prefix.length() &&
1164 current_app[mode_prog_id_prefix.length()] == L'.' &&
1165 std::char_traits<wchar_t>::compare(
1166 mode_prog_id_prefix.c_str(), current_app,
1167 mode_prog_id_prefix.length()) == 0);
1168 });
1169 if (it == &install_static::kInstallModes[install_static::NUM_INSTALL_MODES])
1170 return ShellUtil::NOT_DEFAULT;
1171 other_mode_is_default = true;
1172 }
1173 // This mode is default if it has all of the protocols.
1174 return other_mode_is_default ? ShellUtil::OTHER_MODE_IS_DEFAULT
1175 : ShellUtil::IS_DEFAULT;
1176 }
1177
1178 // Probe using IApplicationAssociationRegistration::QueryAppIsDefault (Vista and
1179 // Windows 7); see ProbeProtocolHandlers.
ProbeAppIsDefaultHandlers(const base::FilePath & chrome_exe,const wchar_t * const * protocols,size_t num_protocols)1180 ShellUtil::DefaultState ProbeAppIsDefaultHandlers(
1181 const base::FilePath& chrome_exe,
1182 const wchar_t* const* protocols,
1183 size_t num_protocols) {
1184 Microsoft::WRL::ComPtr<IApplicationAssociationRegistration> registration;
1185 HRESULT hr =
1186 ::CoCreateInstance(CLSID_ApplicationAssociationRegistration, nullptr,
1187 CLSCTX_INPROC, IID_PPV_ARGS(®istration));
1188 if (FAILED(hr))
1189 return ShellUtil::UNKNOWN_DEFAULT;
1190
1191 base::string16 app_name(GetApplicationName(chrome_exe));
1192
1193 // Generate the app names for this brand's other install modes at both user
1194 // and system levels.
1195 const int current_install_mode_index =
1196 install_static::InstallDetails::Get().install_mode_index();
1197 base::string16 other_app_names[install_static::NUM_INSTALL_MODES * 2];
1198 for (int mode_index = 0; mode_index < install_static::NUM_INSTALL_MODES;
1199 ++mode_index) {
1200 if (mode_index == current_install_mode_index)
1201 continue; // Leave the entry for the current mode empty.
1202 other_app_names[mode_index * 2] = GetApplicationNameForModeAtLevel(
1203 install_static::kInstallModes[mode_index], false);
1204 other_app_names[mode_index * 2 + 1] = GetApplicationNameForModeAtLevel(
1205 install_static::kInstallModes[mode_index], true);
1206 }
1207
1208 // Now check each protocol to see if this brand is default for all. This loop
1209 // terminates when this brand is the default handler for the protocols.
1210 bool other_mode_is_default = false;
1211 for (size_t i = 0; i < num_protocols; ++i) {
1212 const wchar_t* protocol = protocols[i];
1213 BOOL result = TRUE;
1214 // Check the current app name. This will fail (e.g., ERROR_FILE_NOT_FOUND)
1215 // if |app_name| isn't registered.
1216 hr = registration->QueryAppIsDefault(protocol, AT_URLPROTOCOL, AL_EFFECTIVE,
1217 app_name.c_str(), &result);
1218 if (SUCCEEDED(hr) && result)
1219 continue;
1220
1221 // Search for a different install mode that is the default handler.
1222 const auto* it =
1223 std::find_if(std::cbegin(other_app_names), std::cend(other_app_names),
1224 [®istration, protocol](const base::string16& app_name) {
1225 if (app_name.empty())
1226 return false;
1227 BOOL result = TRUE;
1228 HRESULT hr = registration->QueryAppIsDefault(
1229 protocol, AT_URLPROTOCOL, AL_EFFECTIVE,
1230 app_name.c_str(), &result);
1231 return SUCCEEDED(hr) && result;
1232 });
1233 if (it == std::end(other_app_names))
1234 return ShellUtil::NOT_DEFAULT;
1235 other_mode_is_default = true;
1236 }
1237
1238 return other_mode_is_default ? ShellUtil::OTHER_MODE_IS_DEFAULT
1239 : ShellUtil::IS_DEFAULT;
1240 }
1241
1242 // A helper function that probes default protocol handler registration (in a
1243 // manner appropriate for the current version of Windows) to determine if
1244 // Chrome is the default handler for |protocols|. Returns IS_DEFAULT
1245 // only if Chrome is the default for all specified protocols.
ProbeProtocolHandlers(const base::FilePath & chrome_exe,const wchar_t * const * protocols,size_t num_protocols)1246 ShellUtil::DefaultState ProbeProtocolHandlers(const base::FilePath& chrome_exe,
1247 const wchar_t* const* protocols,
1248 size_t num_protocols) {
1249 #if DCHECK_IS_ON()
1250 DCHECK(!num_protocols || protocols);
1251 for (size_t i = 0; i < num_protocols; ++i)
1252 DCHECK(protocols[i] && *protocols[i]);
1253 #endif
1254
1255 const base::win::Version windows_version = base::win::GetVersion();
1256
1257 if (windows_version >= base::win::Version::WIN8)
1258 return ProbeCurrentDefaultHandlers(chrome_exe, protocols, num_protocols);
1259
1260 return ProbeAppIsDefaultHandlers(chrome_exe, protocols, num_protocols);
1261 }
1262
1263 // (Windows 8+) Finds and stores an app shortcuts folder path in *|path|.
1264 // Returns true on success.
GetAppShortcutsFolder(ShellUtil::ShellChange level,base::FilePath * path)1265 bool GetAppShortcutsFolder(ShellUtil::ShellChange level, base::FilePath* path) {
1266 DCHECK(path);
1267 DCHECK_GE(base::win::GetVersion(), base::win::Version::WIN8);
1268
1269 base::FilePath folder;
1270 if (!base::PathService::Get(base::DIR_APP_SHORTCUTS, &folder)) {
1271 LOG(ERROR) << "Could not get application shortcuts location.";
1272 return false;
1273 }
1274
1275 folder = folder.Append(
1276 ShellUtil::GetBrowserModelId(level == ShellUtil::CURRENT_USER));
1277 if (!base::DirectoryExists(folder)) {
1278 VLOG(1) << "No start screen shortcuts.";
1279 return false;
1280 }
1281
1282 *path = folder;
1283 return true;
1284 }
1285
1286 // Shortcut filters for BatchShortcutAction().
1287
1288 using ShortcutFilterCallback =
1289 base::RepeatingCallback<bool(const base::FilePath& shortcut_path,
1290 const base::string16& args)>;
1291
1292 // FilterTargetEq is a shortcut filter that matches only shortcuts that have a
1293 // specific target, and optionally matches shortcuts that have non-empty
1294 // arguments.
1295 class FilterTargetEq {
1296 public:
1297 FilterTargetEq(const base::FilePath& desired_target_exe, bool require_args);
1298
1299 // Returns true if filter rules are satisfied, i.e.:
1300 // - |target_path|'s target == |desired_target_compare_|, and
1301 // - |args| is non-empty (if |require_args_| == true).
1302 bool Match(const base::FilePath& target_path,
1303 const base::string16& args) const;
1304
1305 // A convenience routine to create a callback to call Match().
1306 // The callback is only valid during the lifetime of the FilterTargetEq
1307 // instance.
1308 ShortcutFilterCallback AsShortcutFilterCallback();
1309
1310 private:
1311 InstallUtil::ProgramCompare desired_target_compare_;
1312
1313 bool require_args_;
1314 };
1315
FilterTargetEq(const base::FilePath & desired_target_exe,bool require_args)1316 FilterTargetEq::FilterTargetEq(const base::FilePath& desired_target_exe,
1317 bool require_args)
1318 : desired_target_compare_(desired_target_exe),
1319 require_args_(require_args) {}
1320
Match(const base::FilePath & target_path,const base::string16 & args) const1321 bool FilterTargetEq::Match(const base::FilePath& target_path,
1322 const base::string16& args) const {
1323 if (!desired_target_compare_.EvaluatePath(target_path))
1324 return false;
1325 if (require_args_ && args.empty())
1326 return false;
1327 return true;
1328 }
1329
AsShortcutFilterCallback()1330 ShortcutFilterCallback FilterTargetEq::AsShortcutFilterCallback() {
1331 return base::BindRepeating(&FilterTargetEq::Match, base::Unretained(this));
1332 }
1333
1334 // Shortcut operations for BatchShortcutAction().
1335
1336 using ShortcutOperationCallback =
1337 base::RepeatingCallback<bool(const base::FilePath& shortcut_path)>;
1338
ShortcutOpUnpinFromTaskbar(const base::FilePath & shortcut_path)1339 bool ShortcutOpUnpinFromTaskbar(const base::FilePath& shortcut_path) {
1340 VLOG(1) << "Trying to unpin from taskbar " << shortcut_path.value();
1341 if (!base::win::UnpinShortcutFromTaskbar(shortcut_path)) {
1342 VLOG(1) << shortcut_path.value()
1343 << " wasn't pinned to taskbar (or the unpin failed).";
1344 // No error, since shortcut might not be pinned.
1345 }
1346 return true;
1347 }
1348
ShortcutOpDelete(const base::FilePath & shortcut_path)1349 bool ShortcutOpDelete(const base::FilePath& shortcut_path) {
1350 bool ret = base::DeleteFile(shortcut_path);
1351 LOG_IF(ERROR, !ret) << "Failed to remove " << shortcut_path.value();
1352 return ret;
1353 }
1354
ShortcutOpRetarget(const base::FilePath & old_target,const base::FilePath & new_target,const base::FilePath & shortcut_path)1355 bool ShortcutOpRetarget(const base::FilePath& old_target,
1356 const base::FilePath& new_target,
1357 const base::FilePath& shortcut_path) {
1358 base::win::ShortcutProperties new_prop;
1359 new_prop.set_target(new_target);
1360
1361 // If the old icon matches old target, then update icon while keeping the old
1362 // icon index. Non-fatal if we fail to get the old icon.
1363 base::win::ShortcutProperties old_prop;
1364 if (base::win::ResolveShortcutProperties(
1365 shortcut_path, base::win::ShortcutProperties::PROPERTIES_ICON,
1366 &old_prop)) {
1367 if (InstallUtil::ProgramCompare(old_target).EvaluatePath(old_prop.icon))
1368 new_prop.set_icon(new_target, old_prop.icon_index);
1369 } else {
1370 LOG(ERROR) << "Failed to resolve " << shortcut_path.value();
1371 }
1372
1373 bool result = base::win::CreateOrUpdateShortcutLink(
1374 shortcut_path, new_prop, base::win::SHORTCUT_UPDATE_EXISTING);
1375 LOG_IF(ERROR, !result) << "Failed to retarget " << shortcut_path.value();
1376 return result;
1377 }
1378
ShortcutOpListOrRemoveUnknownArgs(bool do_removal,std::vector<std::pair<base::FilePath,base::string16>> * shortcuts,const base::FilePath & shortcut_path)1379 bool ShortcutOpListOrRemoveUnknownArgs(
1380 bool do_removal,
1381 std::vector<std::pair<base::FilePath, base::string16>>* shortcuts,
1382 const base::FilePath& shortcut_path) {
1383 base::string16 args;
1384 if (!base::win::ResolveShortcut(shortcut_path, nullptr, &args))
1385 return false;
1386
1387 base::CommandLine current_args(base::CommandLine::FromString(
1388 base::StringPrintf(L"unused_program %ls", args.c_str())));
1389 const char* const kept_switches[] = {
1390 switches::kApp,
1391 switches::kAppId,
1392 switches::kProfileDirectory,
1393 };
1394 base::CommandLine desired_args(base::CommandLine::NO_PROGRAM);
1395 desired_args.CopySwitchesFrom(current_args, kept_switches,
1396 base::size(kept_switches));
1397 if (desired_args.argv().size() == current_args.argv().size())
1398 return true;
1399 if (shortcuts)
1400 shortcuts->push_back(std::make_pair(shortcut_path, args));
1401 if (!do_removal)
1402 return true;
1403 base::win::ShortcutProperties updated_properties;
1404 updated_properties.set_arguments(desired_args.GetArgumentsString());
1405 return base::win::CreateOrUpdateShortcutLink(
1406 shortcut_path, updated_properties, base::win::SHORTCUT_UPDATE_EXISTING);
1407 }
1408
ShortcutOpResetAttributes(const base::FilePath & file_path)1409 bool ShortcutOpResetAttributes(const base::FilePath& file_path) {
1410 const DWORD kAllowedAttributes =
1411 FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_REPARSE_POINT;
1412 DWORD attributes = ::GetFileAttributes(file_path.value().c_str());
1413 if (attributes == INVALID_FILE_ATTRIBUTES)
1414 return false;
1415 if ((attributes & (~kAllowedAttributes)) == 0)
1416 return true;
1417 return ::SetFileAttributes(file_path.value().c_str(),
1418 attributes & kAllowedAttributes);
1419 }
1420
1421 // {|location|, |level|} determine |shortcut_folder|.
1422 // For each shortcut in |shortcut_folder| that match |shortcut_filter|, apply
1423 // |shortcut_operation|. Returns true if all operations are successful.
1424 // All intended operations are attempted, even if failures occur.
1425 // This method will abort and return false if |cancel| is non-nullptr and gets
1426 // set at any point during this call.
BatchShortcutAction(const ShortcutFilterCallback & shortcut_filter,const ShortcutOperationCallback & shortcut_operation,ShellUtil::ShortcutLocation location,ShellUtil::ShellChange level,const scoped_refptr<ShellUtil::SharedCancellationFlag> & cancel)1427 bool BatchShortcutAction(
1428 const ShortcutFilterCallback& shortcut_filter,
1429 const ShortcutOperationCallback& shortcut_operation,
1430 ShellUtil::ShortcutLocation location,
1431 ShellUtil::ShellChange level,
1432 const scoped_refptr<ShellUtil::SharedCancellationFlag>& cancel) {
1433 DCHECK(!shortcut_operation.is_null());
1434
1435 // There is no system-level Quick Launch shortcut folder.
1436 if (level == ShellUtil::SYSTEM_LEVEL &&
1437 location == ShellUtil::SHORTCUT_LOCATION_QUICK_LAUNCH) {
1438 return true;
1439 }
1440
1441 base::FilePath shortcut_folder;
1442 if (!ShellUtil::GetShortcutPath(location, level, &shortcut_folder)) {
1443 LOG(WARNING) << "Cannot find path at location " << location;
1444 return false;
1445 }
1446
1447 bool success = true;
1448 base::FileEnumerator enumerator(shortcut_folder, false,
1449 base::FileEnumerator::FILES,
1450 base::string16(L"*") + installer::kLnkExt);
1451 base::FilePath target_path;
1452 base::string16 args;
1453 for (base::FilePath shortcut_path = enumerator.Next(); !shortcut_path.empty();
1454 shortcut_path = enumerator.Next()) {
1455 if (cancel.get() && cancel->data.IsSet())
1456 return false;
1457 if (base::win::ResolveShortcut(shortcut_path, &target_path, &args)) {
1458 if (shortcut_filter.Run(target_path, args) &&
1459 !shortcut_operation.Run(shortcut_path)) {
1460 success = false;
1461 }
1462 } else {
1463 LOG(ERROR) << "Cannot resolve shortcut at " << shortcut_path.value();
1464 success = false;
1465 }
1466 }
1467 return success;
1468 }
1469
1470 // If the folder specified by {|location|, |level|} is empty, remove it.
1471 // Otherwise do nothing. Returns true on success, including the vacuous case
1472 // where no deletion occurred because directory is non-empty.
RemoveShortcutFolderIfEmpty(ShellUtil::ShortcutLocation location,ShellUtil::ShellChange level)1473 bool RemoveShortcutFolderIfEmpty(ShellUtil::ShortcutLocation location,
1474 ShellUtil::ShellChange level) {
1475 // Explicitly allow locations, since accidental calls can be very harmful.
1476 if (location !=
1477 ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR_DEPRECATED &&
1478 location != ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR &&
1479 location != ShellUtil::SHORTCUT_LOCATION_APP_SHORTCUTS) {
1480 NOTREACHED();
1481 return false;
1482 }
1483
1484 base::FilePath shortcut_folder;
1485 if (!ShellUtil::GetShortcutPath(location, level, &shortcut_folder)) {
1486 LOG(WARNING) << "Cannot find path at location " << location;
1487 return false;
1488 }
1489 if (base::IsDirectoryEmpty(shortcut_folder) &&
1490 !base::DeletePathRecursively(shortcut_folder)) {
1491 LOG(ERROR) << "Cannot remove folder " << shortcut_folder.value();
1492 return false;
1493 }
1494 return true;
1495 }
1496
1497 // Return a shortened version of |component|. Cut in the middle to try
1498 // to avoid losing the unique parts of |component| (which are usually
1499 // at the beginning or end for things like usernames and paths).
ShortenAppModelIdComponent(const base::string16 & component,int desired_length)1500 base::string16 ShortenAppModelIdComponent(const base::string16& component,
1501 int desired_length) {
1502 return component.substr(0, desired_length / 2) +
1503 component.substr(component.length() - ((desired_length + 1) / 2));
1504 }
1505
RegisterChromeBrowserImpl(const base::FilePath & chrome_exe,const base::string16 & unique_suffix,bool elevate_if_not_admin,bool best_effort_no_rollback)1506 bool RegisterChromeBrowserImpl(const base::FilePath& chrome_exe,
1507 const base::string16& unique_suffix,
1508 bool elevate_if_not_admin,
1509 bool best_effort_no_rollback) {
1510 base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
1511
1512 base::string16 suffix;
1513 if (!unique_suffix.empty()) {
1514 suffix = unique_suffix;
1515 } else if (command_line.HasSwitch(
1516 installer::switches::kRegisterChromeBrowserSuffix)) {
1517 suffix = command_line.GetSwitchValueNative(
1518 installer::switches::kRegisterChromeBrowserSuffix);
1519 } else if (!GetInstallationSpecificSuffix(chrome_exe, &suffix)) {
1520 return false;
1521 }
1522
1523 RemoveRunVerbOnWindows8();
1524
1525 bool user_level = InstallUtil::IsPerUserInstall();
1526 HKEY root = DetermineRegistrationRoot(user_level);
1527
1528 // Look only in HKLM for system-level installs (otherwise, if a user-level
1529 // install is also present, it will lead IsChromeRegistered() to think this
1530 // system-level install isn't registered properly as it is shadowed by the
1531 // user-level install's registrations).
1532 uint32_t look_for_in = user_level ? RegistryEntry::LOOK_IN_HKCU_THEN_HKLM
1533 : RegistryEntry::LOOK_IN_HKLM;
1534
1535 // Check if chrome is already registered with this suffix.
1536 if (IsChromeRegistered(chrome_exe, suffix, look_for_in))
1537 return true;
1538
1539 // Ensure that the shell is notified of the mutations below. Specific exit
1540 // points may disable this if no mutations are made.
1541 base::ScopedClosureRunner notify_on_exit(base::BindOnce([] {
1542 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);
1543 }));
1544
1545 // Do the full registration at user-level or if the user is an admin.
1546 if (root == HKEY_CURRENT_USER || IsUserAnAdmin()) {
1547 std::vector<std::unique_ptr<RegistryEntry>> progid_and_appreg_entries;
1548 std::vector<std::unique_ptr<RegistryEntry>> shell_entries;
1549 GetChromeProgIdEntries(chrome_exe, suffix, &progid_and_appreg_entries);
1550 GetChromeAppRegistrationEntries(chrome_exe, suffix,
1551 &progid_and_appreg_entries);
1552 GetShellIntegrationEntries(chrome_exe, suffix, &shell_entries);
1553 return ShellUtil::AddRegistryEntries(root, progid_and_appreg_entries,
1554 best_effort_no_rollback) &&
1555 ShellUtil::AddRegistryEntries(root, shell_entries,
1556 best_effort_no_rollback);
1557 }
1558 // The installer is responsible for registration for system-level installs, so
1559 // never try to do it here. Getting to this point for a system-level install
1560 // likely means that IsChromeRegistered thinks registration is broken due to
1561 // localization issues (see https://crbug.com/717913#c18). It likely is not,
1562 // so return success to allow Chrome to be made default.
1563 if (!user_level) {
1564 notify_on_exit.Release().Reset();
1565 return true;
1566 }
1567 // Try to elevate and register if requested for per-user installs if the user
1568 // is not an admin.
1569 if (elevate_if_not_admin &&
1570 ElevateAndRegisterChrome(chrome_exe, suffix, base::string16())) {
1571 return true;
1572 }
1573 // If we got to this point then all we can do is create ProgId and basic app
1574 // registrations under HKCU.
1575 std::vector<std::unique_ptr<RegistryEntry>> entries;
1576 GetChromeProgIdEntries(chrome_exe, base::string16(), &entries);
1577 // Prefer to use |suffix|; unless Chrome's ProgIds are already registered with
1578 // no suffix (as per the old registration style): in which case some other
1579 // registry entries could refer to them and since we were not able to set our
1580 // HKLM entries above, we are better off not altering these here.
1581 if (!AreEntriesAsDesired(entries, RegistryEntry::LOOK_IN_HKCU)) {
1582 if (!suffix.empty()) {
1583 entries.clear();
1584 GetChromeProgIdEntries(chrome_exe, suffix, &entries);
1585 GetChromeAppRegistrationEntries(chrome_exe, suffix, &entries);
1586 }
1587 return ShellUtil::AddRegistryEntries(HKEY_CURRENT_USER, entries,
1588 best_effort_no_rollback);
1589 }
1590 // The ProgId is registered unsuffixed in HKCU, also register the app with
1591 // Windows in HKCU (this was not done in the old registration style and thus
1592 // needs to be done after the above check for the unsuffixed registration).
1593 entries.clear();
1594 GetChromeAppRegistrationEntries(chrome_exe, base::string16(), &entries);
1595 return ShellUtil::AddRegistryEntries(HKEY_CURRENT_USER, entries,
1596 best_effort_no_rollback);
1597 }
1598
1599 } // namespace
1600
1601 const wchar_t* ShellUtil::kRegDefaultIcon = L"\\DefaultIcon";
1602 const wchar_t* ShellUtil::kRegShellPath = L"\\shell";
1603 const wchar_t* ShellUtil::kRegShellOpen = L"\\shell\\open\\command";
1604 const wchar_t* ShellUtil::kRegStartMenuInternet =
1605 L"Software\\Clients\\StartMenuInternet";
1606 const wchar_t* ShellUtil::kRegClasses = L"Software\\Classes";
1607 const wchar_t* ShellUtil::kRegRegisteredApplications =
1608 L"Software\\RegisteredApplications";
1609 const wchar_t* ShellUtil::kRegVistaUrlPrefs =
1610 L"Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
1611 L"http\\UserChoice";
1612 const wchar_t* ShellUtil::kAppPathsRegistryKey =
1613 L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths";
1614 const wchar_t* ShellUtil::kAppPathsRegistryPathName = L"Path";
1615
1616 const wchar_t* ShellUtil::kDefaultFileAssociations[] = {
1617 L".htm", L".html", L".shtml", L".xht", L".xhtml", nullptr};
1618 const wchar_t* ShellUtil::kPotentialFileAssociations[] = {
1619 L".htm", L".html", L".pdf", L".shtml", L".svg",
1620 L".xht", L".xhtml", L".webp", nullptr};
1621 const wchar_t* ShellUtil::kBrowserProtocolAssociations[] = {L"ftp", L"http",
1622 L"https", nullptr};
1623 const wchar_t* ShellUtil::kPotentialProtocolAssociations[] = {
1624 L"ftp", L"http", L"https", L"irc", L"mailto", L"mms", L"news", L"nntp",
1625 L"sms", L"smsto", L"snews", L"tel", L"urn", L"webcal", nullptr};
1626 const wchar_t* ShellUtil::kRegUrlProtocol = L"URL Protocol";
1627 const wchar_t* ShellUtil::kRegApplication = L"\\Application";
1628 const wchar_t* ShellUtil::kRegAppUserModelId = L"AppUserModelId";
1629 const wchar_t* ShellUtil::kRegApplicationDescription =
1630 L"ApplicationDescription";
1631 const wchar_t* ShellUtil::kRegApplicationName = L"ApplicationName";
1632 const wchar_t* ShellUtil::kRegApplicationIcon = L"ApplicationIcon";
1633 const wchar_t* ShellUtil::kRegApplicationCompany = L"ApplicationCompany";
1634 const wchar_t* ShellUtil::kRegExePath = L"\\.exe";
1635 const wchar_t* ShellUtil::kRegVerbOpen = L"open";
1636 const wchar_t* ShellUtil::kRegVerbOpenNewWindow = L"opennewwindow";
1637 const wchar_t* ShellUtil::kRegVerbRun = L"run";
1638 const wchar_t* ShellUtil::kRegCommand = L"command";
1639 const wchar_t* ShellUtil::kRegDelegateExecute = L"DelegateExecute";
1640 const wchar_t* ShellUtil::kRegOpenWithProgids = L"OpenWithProgids";
1641
ShortcutProperties(ShellChange level_in)1642 ShellUtil::ShortcutProperties::ShortcutProperties(ShellChange level_in)
1643 : level(level_in), icon_index(0), pin_to_taskbar(false), options(0U) {}
1644
1645 ShellUtil::ShortcutProperties::ShortcutProperties(
1646 const ShortcutProperties& other) = default;
1647
~ShortcutProperties()1648 ShellUtil::ShortcutProperties::~ShortcutProperties() {}
1649
1650 ShellUtil::FileAssociationsAndAppName::FileAssociationsAndAppName() = default;
1651
1652 ShellUtil::FileAssociationsAndAppName::FileAssociationsAndAppName(
1653 FileAssociationsAndAppName&& other) = default;
1654
1655 ShellUtil::FileAssociationsAndAppName::~FileAssociationsAndAppName() = default;
1656
QuickIsChromeRegisteredInHKLM(const base::FilePath & chrome_exe,const base::string16 & suffix)1657 bool ShellUtil::QuickIsChromeRegisteredInHKLM(const base::FilePath& chrome_exe,
1658 const base::string16& suffix) {
1659 return QuickIsChromeRegistered(chrome_exe, suffix,
1660 CONFIRM_SHELL_REGISTRATION_IN_HKLM);
1661 }
1662
ShortcutLocationIsSupported(ShortcutLocation location)1663 bool ShellUtil::ShortcutLocationIsSupported(ShortcutLocation location) {
1664 switch (location) {
1665 case SHORTCUT_LOCATION_DESKTOP: // Falls through.
1666 case SHORTCUT_LOCATION_QUICK_LAUNCH: // Falls through.
1667 case SHORTCUT_LOCATION_START_MENU_ROOT: // Falls through.
1668 case SHORTCUT_LOCATION_START_MENU_CHROME_DIR_DEPRECATED: // Falls through.
1669 case SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR: // Falls through.
1670 case SHORTCUT_LOCATION_STARTUP:
1671 return true;
1672 case SHORTCUT_LOCATION_TASKBAR_PINS:
1673 return base::win::GetVersion() >= base::win::Version::WIN7;
1674 case SHORTCUT_LOCATION_APP_SHORTCUTS:
1675 return base::win::GetVersion() >= base::win::Version::WIN8;
1676 default:
1677 NOTREACHED();
1678 return false;
1679 }
1680 }
1681
GetShortcutPath(ShortcutLocation location,ShellChange level,base::FilePath * path)1682 bool ShellUtil::GetShortcutPath(ShortcutLocation location,
1683 ShellChange level,
1684 base::FilePath* path) {
1685 DCHECK(path);
1686 int dir_key = -1;
1687 base::string16 folder_to_append;
1688 switch (location) {
1689 case SHORTCUT_LOCATION_DESKTOP:
1690 dir_key = (level == CURRENT_USER) ? base::DIR_USER_DESKTOP
1691 : base::DIR_COMMON_DESKTOP;
1692 break;
1693 case SHORTCUT_LOCATION_QUICK_LAUNCH:
1694 // There is no support for a system-level Quick Launch shortcut.
1695 DCHECK_EQ(level, CURRENT_USER);
1696 dir_key = base::DIR_USER_QUICK_LAUNCH;
1697 break;
1698 case SHORTCUT_LOCATION_START_MENU_ROOT:
1699 dir_key = (level == CURRENT_USER) ? base::DIR_START_MENU
1700 : base::DIR_COMMON_START_MENU;
1701 break;
1702 case SHORTCUT_LOCATION_START_MENU_CHROME_DIR_DEPRECATED:
1703 dir_key = (level == CURRENT_USER) ? base::DIR_START_MENU
1704 : base::DIR_COMMON_START_MENU;
1705 folder_to_append = InstallUtil::GetChromeShortcutDirNameDeprecated();
1706 break;
1707 case SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR:
1708 dir_key = (level == CURRENT_USER) ? base::DIR_START_MENU
1709 : base::DIR_COMMON_START_MENU;
1710 folder_to_append = InstallUtil::GetChromeAppsShortcutDirName();
1711 break;
1712 case SHORTCUT_LOCATION_TASKBAR_PINS:
1713 dir_key = base::DIR_TASKBAR_PINS;
1714 break;
1715 case SHORTCUT_LOCATION_APP_SHORTCUTS:
1716 // TODO(huangs): Move GetAppShortcutsFolder() logic into base_paths_win.
1717 return GetAppShortcutsFolder(level, path);
1718 case SHORTCUT_LOCATION_STARTUP:
1719 dir_key = (level == CURRENT_USER) ? base::DIR_USER_STARTUP
1720 : base::DIR_COMMON_STARTUP;
1721 break;
1722
1723 default:
1724 NOTREACHED();
1725 return false;
1726 }
1727
1728 if (!base::PathService::Get(dir_key, path) || path->empty()) {
1729 NOTREACHED() << dir_key;
1730 return false;
1731 }
1732
1733 if (!folder_to_append.empty())
1734 *path = path->Append(folder_to_append);
1735
1736 return true;
1737 }
1738
1739 // Modifies a ShortcutProperties object by adding default values to
1740 // uninitialized members. Tries to assign:
1741 // - target: |target_exe|.
1742 // - icon: from |target_exe|.
1743 // - icon_index: the browser's icon index
1744 // - app_id: the browser model id for the current install.
1745 // - description: the browser's app description.
1746 // static
AddDefaultShortcutProperties(const base::FilePath & target_exe,ShortcutProperties * properties)1747 void ShellUtil::AddDefaultShortcutProperties(const base::FilePath& target_exe,
1748 ShortcutProperties* properties) {
1749 if (!properties->has_target())
1750 properties->set_target(target_exe);
1751
1752 if (!properties->has_icon())
1753 properties->set_icon(target_exe, install_static::GetIconResourceIndex());
1754
1755 if (!properties->has_app_id()) {
1756 properties->set_app_id(
1757 GetBrowserModelId(!install_static::IsSystemInstall()));
1758 }
1759
1760 if (!properties->has_description())
1761 properties->set_description(InstallUtil::GetAppDescription());
1762 }
1763
MoveExistingShortcut(ShortcutLocation old_location,ShortcutLocation new_location,const ShortcutProperties & properties)1764 bool ShellUtil::MoveExistingShortcut(ShortcutLocation old_location,
1765 ShortcutLocation new_location,
1766 const ShortcutProperties& properties) {
1767 // Explicitly allow locations to which this is applicable.
1768 if (old_location != SHORTCUT_LOCATION_START_MENU_CHROME_DIR_DEPRECATED ||
1769 new_location != SHORTCUT_LOCATION_START_MENU_ROOT) {
1770 NOTREACHED();
1771 return false;
1772 }
1773
1774 base::string16 shortcut_name(ExtractShortcutNameFromProperties(properties));
1775
1776 base::FilePath old_shortcut_path;
1777 base::FilePath new_shortcut_path;
1778 GetShortcutPath(old_location, properties.level, &old_shortcut_path);
1779 GetShortcutPath(new_location, properties.level, &new_shortcut_path);
1780 old_shortcut_path = old_shortcut_path.Append(shortcut_name);
1781 new_shortcut_path = new_shortcut_path.Append(shortcut_name);
1782
1783 bool result = base::Move(old_shortcut_path, new_shortcut_path);
1784 RemoveShortcutFolderIfEmpty(old_location, properties.level);
1785 return result;
1786 }
1787
CreateOrUpdateShortcut(ShortcutLocation location,const ShortcutProperties & properties,ShortcutOperation operation)1788 bool ShellUtil::CreateOrUpdateShortcut(ShortcutLocation location,
1789 const ShortcutProperties& properties,
1790 ShortcutOperation operation) {
1791 // Explicitly allow locations to which this is applicable.
1792 if (location != SHORTCUT_LOCATION_DESKTOP &&
1793 location != SHORTCUT_LOCATION_QUICK_LAUNCH &&
1794 location != SHORTCUT_LOCATION_START_MENU_ROOT &&
1795 location != SHORTCUT_LOCATION_START_MENU_CHROME_DIR_DEPRECATED &&
1796 location != SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR) {
1797 NOTREACHED();
1798 return false;
1799 }
1800
1801 // |pin_to_taskbar| is only acknowledged when first creating the shortcut.
1802 DCHECK(!properties.pin_to_taskbar ||
1803 operation == SHELL_SHORTCUT_CREATE_ALWAYS ||
1804 operation == SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL);
1805
1806 base::FilePath user_shortcut_path;
1807 base::FilePath system_shortcut_path;
1808 if (location == SHORTCUT_LOCATION_QUICK_LAUNCH) {
1809 // There is no system-level shortcut for Quick Launch.
1810 DCHECK_EQ(properties.level, CURRENT_USER);
1811 } else if (!GetShortcutPath(location, SYSTEM_LEVEL, &system_shortcut_path)) {
1812 NOTREACHED();
1813 return false;
1814 }
1815
1816 base::string16 shortcut_name(ExtractShortcutNameFromProperties(properties));
1817 system_shortcut_path = system_shortcut_path.Append(shortcut_name);
1818
1819 base::FilePath* chosen_path;
1820 bool should_install_shortcut = true;
1821 if (properties.level == SYSTEM_LEVEL) {
1822 // Install the system-level shortcut if requested.
1823 chosen_path = &system_shortcut_path;
1824 } else if (operation != SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL ||
1825 system_shortcut_path.empty() ||
1826 !base::PathExists(system_shortcut_path)) {
1827 // Otherwise install the user-level shortcut, unless the system-level
1828 // variant of this shortcut is present on the machine and |operation| states
1829 // not to create a user-level shortcut in that case.
1830 if (!GetShortcutPath(location, CURRENT_USER, &user_shortcut_path)) {
1831 NOTREACHED();
1832 return false;
1833 }
1834 user_shortcut_path = user_shortcut_path.Append(shortcut_name);
1835 chosen_path = &user_shortcut_path;
1836 } else {
1837 // Do not install any shortcut if we are told to install a user-level
1838 // shortcut, but the system-level variant of that shortcut is present.
1839 // Other actions (e.g., pinning) can still happen with respect to the
1840 // existing system-level shortcut however.
1841 chosen_path = &system_shortcut_path;
1842 should_install_shortcut = false;
1843 }
1844
1845 if (chosen_path == nullptr || chosen_path->empty()) {
1846 NOTREACHED();
1847 return false;
1848 }
1849
1850 base::win::ShortcutOperation shortcut_operation =
1851 TranslateShortcutOperation(operation);
1852 bool success = true;
1853 if (should_install_shortcut) {
1854 // Make sure the parent directories exist when creating the shortcut.
1855 if (shortcut_operation == base::win::SHORTCUT_CREATE_ALWAYS &&
1856 !base::CreateDirectory(chosen_path->DirName())) {
1857 NOTREACHED();
1858 return false;
1859 }
1860
1861 base::win::ShortcutProperties shortcut_properties(
1862 TranslateShortcutProperties(properties));
1863 success = base::win::CreateOrUpdateShortcutLink(
1864 *chosen_path, shortcut_properties, shortcut_operation);
1865 }
1866
1867 if (success && shortcut_operation == base::win::SHORTCUT_CREATE_ALWAYS &&
1868 properties.pin_to_taskbar && base::win::CanPinShortcutToTaskbar()) {
1869 bool pinned = base::win::PinShortcutToTaskbar(*chosen_path);
1870 LOG_IF(ERROR, !pinned) << "Failed to pin to taskbar "
1871 << chosen_path->value();
1872 }
1873
1874 return success;
1875 }
1876
FormatIconLocation(const base::FilePath & icon_path,int icon_index)1877 base::string16 ShellUtil::FormatIconLocation(const base::FilePath& icon_path,
1878 int icon_index) {
1879 base::string16 icon_string(icon_path.value());
1880 icon_string.append(L",");
1881 icon_string.append(base::NumberToString16(icon_index));
1882 return icon_string;
1883 }
1884
GetChromeShellOpenCmd(const base::FilePath & chrome_exe)1885 base::string16 ShellUtil::GetChromeShellOpenCmd(
1886 const base::FilePath& chrome_exe) {
1887 return base::CommandLine(chrome_exe).GetCommandLineStringForShell();
1888 }
1889
GetChromeDelegateCommand(const base::FilePath & chrome_exe)1890 base::string16 ShellUtil::GetChromeDelegateCommand(
1891 const base::FilePath& chrome_exe) {
1892 return L"\"" + chrome_exe.value() + L"\" -- %*";
1893 }
1894
GetRegisteredBrowsers(std::map<base::string16,base::string16> * browsers)1895 void ShellUtil::GetRegisteredBrowsers(
1896 std::map<base::string16, base::string16>* browsers) {
1897 DCHECK(browsers);
1898
1899 const base::string16 base_key(kRegStartMenuInternet);
1900 base::string16 client_path;
1901 RegKey key;
1902 base::string16 name;
1903 base::string16 command;
1904
1905 // HKCU has precedence over HKLM for these registrations: http://goo.gl/xjczJ.
1906 // Look in HKCU second to override any identical values found in HKLM.
1907 const HKEY roots[] = {HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER};
1908 for (const HKEY root : roots) {
1909 for (base::win::RegistryKeyIterator iter(root, base_key.c_str());
1910 iter.Valid(); ++iter) {
1911 client_path.assign(base_key).append(1, L'\\').append(iter.Name());
1912 // Read the browser's name (localized according to install language).
1913 if (key.Open(root, client_path.c_str(), KEY_QUERY_VALUE) !=
1914 ERROR_SUCCESS ||
1915 key.ReadValue(nullptr, &name) != ERROR_SUCCESS || name.empty() ||
1916 name.find(install_static::GetBaseAppName()) != base::string16::npos) {
1917 continue;
1918 }
1919 // Read the browser's reinstall command.
1920 if (key.Open(root, (client_path + L"\\InstallInfo").c_str(),
1921 KEY_QUERY_VALUE) == ERROR_SUCCESS &&
1922 key.ReadValue(kReinstallCommand, &command) == ERROR_SUCCESS &&
1923 !command.empty()) {
1924 (*browsers)[name] = command;
1925 }
1926 }
1927 }
1928 }
1929
GetCurrentInstallationSuffix(const base::FilePath & chrome_exe)1930 base::string16 ShellUtil::GetCurrentInstallationSuffix(
1931 const base::FilePath& chrome_exe) {
1932 // This method is somewhat the opposite of GetInstallationSpecificSuffix().
1933 // In this case we are not trying to determine the current suffix for the
1934 // upcoming installation (i.e. not trying to stick to a currently bad
1935 // registration style if one is present).
1936 // Here we want to determine which suffix we should use at run-time.
1937 // In order of preference, we prefer (for user-level installs):
1938 // 1) Base 32 encoding of the md5 hash of the user's sid (new-style).
1939 // 2) Username (old-style).
1940 // 3) Unsuffixed (even worse).
1941 base::string16 tested_suffix;
1942 if (InstallUtil::IsPerUserInstall() &&
1943 (!GetUserSpecificRegistrySuffix(&tested_suffix) ||
1944 !QuickIsChromeRegistered(chrome_exe, tested_suffix,
1945 CONFIRM_PROGID_REGISTRATION)) &&
1946 (!GetOldUserSpecificRegistrySuffix(&tested_suffix) ||
1947 !QuickIsChromeRegistered(chrome_exe, tested_suffix,
1948 CONFIRM_PROGID_REGISTRATION)) &&
1949 !QuickIsChromeRegistered(chrome_exe, tested_suffix.erase(),
1950 CONFIRM_PROGID_REGISTRATION)) {
1951 // If Chrome is not registered under any of the possible suffixes (e.g.
1952 // tests, Canary, etc.): use the new-style suffix at run-time.
1953 if (!GetUserSpecificRegistrySuffix(&tested_suffix))
1954 NOTREACHED();
1955 }
1956 return tested_suffix;
1957 }
1958
GetBrowserModelId(bool is_per_user_install)1959 base::string16 ShellUtil::GetBrowserModelId(bool is_per_user_install) {
1960 base::string16 app_id(install_static::GetBaseAppId());
1961 base::string16 suffix;
1962
1963 // TODO(robertshield): Temporary hack to make the kRegisterChromeBrowserSuffix
1964 // apply to all registry values computed down in these murky depths.
1965 base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
1966 if (command_line.HasSwitch(
1967 installer::switches::kRegisterChromeBrowserSuffix)) {
1968 suffix = command_line.GetSwitchValueNative(
1969 installer::switches::kRegisterChromeBrowserSuffix);
1970 } else if (is_per_user_install && !GetUserSpecificRegistrySuffix(&suffix)) {
1971 NOTREACHED();
1972 }
1973 app_id.append(suffix);
1974 if (app_id.length() <= installer::kMaxAppModelIdLength)
1975 return app_id;
1976 return ShortenAppModelIdComponent(app_id, installer::kMaxAppModelIdLength);
1977 }
1978
BuildAppUserModelId(const std::vector<base::string16> & components)1979 base::string16 ShellUtil::BuildAppUserModelId(
1980 const std::vector<base::string16>& components) {
1981 DCHECK_GT(components.size(), 0U);
1982
1983 // Find the maximum numbers of characters allowed in each component
1984 // (accounting for the dots added between each component).
1985 const size_t available_chars =
1986 installer::kMaxAppModelIdLength - (components.size() - 1);
1987 const size_t max_component_length = available_chars / components.size();
1988
1989 // |max_component_length| should be at least 2; otherwise the truncation logic
1990 // below breaks.
1991 if (max_component_length < 2U) {
1992 NOTREACHED();
1993 return (*components.begin()).substr(0, installer::kMaxAppModelIdLength);
1994 }
1995
1996 base::string16 app_id;
1997 app_id.reserve(installer::kMaxAppModelIdLength);
1998 for (std::vector<base::string16>::const_iterator it = components.begin();
1999 it != components.end(); ++it) {
2000 if (it != components.begin())
2001 app_id.push_back(L'.');
2002
2003 const base::string16& component = *it;
2004 DCHECK(!component.empty());
2005 if (component.length() > max_component_length) {
2006 app_id.append(
2007 ShortenAppModelIdComponent(component, max_component_length));
2008 } else {
2009 app_id.append(component);
2010 }
2011 }
2012 // No spaces are allowed in the AppUserModelId according to MSDN.
2013 base::ReplaceChars(app_id, base::ASCIIToUTF16(" "), base::ASCIIToUTF16("_"),
2014 &app_id);
2015 return app_id;
2016 }
2017
GetChromeDefaultState()2018 ShellUtil::DefaultState ShellUtil::GetChromeDefaultState() {
2019 base::FilePath app_path;
2020 if (!base::PathService::Get(base::FILE_EXE, &app_path)) {
2021 NOTREACHED();
2022 return UNKNOWN_DEFAULT;
2023 }
2024
2025 return GetChromeDefaultStateFromPath(app_path);
2026 }
2027
GetChromeDefaultStateFromPath(const base::FilePath & chrome_exe)2028 ShellUtil::DefaultState ShellUtil::GetChromeDefaultStateFromPath(
2029 const base::FilePath& chrome_exe) {
2030 // When we check for default browser we don't necessarily want to count file
2031 // type handlers and icons as having changed the default browser status,
2032 // since the user may have changed their shell settings to cause HTML files
2033 // to open with a text editor for example. We also don't want to aggressively
2034 // claim FTP, since the user may have a separate FTP client. It is an open
2035 // question as to how to "heal" these settings. Perhaps the user should just
2036 // re-run the installer or run with the --set-default-browser command line
2037 // flag. There is doubtless some other key we can hook into to cause "Repair"
2038 // to show up in Add/Remove programs for us.
2039 static const wchar_t* const kChromeProtocols[] = {L"http", L"https"};
2040 DefaultState default_state = ProbeProtocolHandlers(
2041 chrome_exe, kChromeProtocols, base::size(kChromeProtocols));
2042 UpdateDefaultBrowserBeaconWithState(default_state);
2043 return default_state;
2044 }
2045
GetChromeDefaultProtocolClientState(const base::string16 & protocol)2046 ShellUtil::DefaultState ShellUtil::GetChromeDefaultProtocolClientState(
2047 const base::string16& protocol) {
2048 if (protocol.empty())
2049 return UNKNOWN_DEFAULT;
2050
2051 base::FilePath chrome_exe;
2052 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
2053 NOTREACHED();
2054 return UNKNOWN_DEFAULT;
2055 }
2056
2057 const wchar_t* const protocols[] = {protocol.c_str()};
2058 return ProbeProtocolHandlers(chrome_exe, protocols, base::size(protocols));
2059 }
2060
2061 // static
CanMakeChromeDefaultUnattended()2062 bool ShellUtil::CanMakeChromeDefaultUnattended() {
2063 return base::win::GetVersion() < base::win::Version::WIN8;
2064 }
2065
2066 // static
GetInteractiveSetDefaultMode()2067 ShellUtil::InteractiveSetDefaultMode ShellUtil::GetInteractiveSetDefaultMode() {
2068 DCHECK(!CanMakeChromeDefaultUnattended());
2069
2070 if (base::win::GetVersion() >= base::win::Version::WIN10)
2071 return InteractiveSetDefaultMode::SYSTEM_SETTINGS;
2072
2073 return InteractiveSetDefaultMode::INTENT_PICKER;
2074 }
2075
MakeChromeDefault(int shell_change,const base::FilePath & chrome_exe,bool elevate_if_not_admin)2076 bool ShellUtil::MakeChromeDefault(int shell_change,
2077 const base::FilePath& chrome_exe,
2078 bool elevate_if_not_admin) {
2079 DCHECK(!(shell_change & SYSTEM_LEVEL) || IsUserAnAdmin());
2080
2081 if (!install_static::SupportsSetAsDefaultBrowser())
2082 return false;
2083
2084 // Windows 8 does not permit making a browser default just like that.
2085 // This process needs to be routed through the system's UI. Use
2086 // ShowMakeChromeDefaultSystemUI instead (below).
2087 if (!CanMakeChromeDefaultUnattended()) {
2088 return false;
2089 }
2090
2091 if (!RegisterChromeBrowser(chrome_exe, base::string16(),
2092 elevate_if_not_admin)) {
2093 return false;
2094 }
2095
2096 bool ret = true;
2097 // First use the new "recommended" way on Vista to make Chrome default
2098 // browser.
2099 base::string16 app_name = GetApplicationName(chrome_exe);
2100
2101 // On Windows 7 we still can set ourselves via the the
2102 // IApplicationAssociationRegistration interface.
2103 VLOG(1) << "Registering Chrome as default browser on Windows 7.";
2104 Microsoft::WRL::ComPtr<IApplicationAssociationRegistration> pAAR;
2105 HRESULT hr = ::CoCreateInstance(CLSID_ApplicationAssociationRegistration,
2106 nullptr, CLSCTX_INPROC, IID_PPV_ARGS(&pAAR));
2107 if (SUCCEEDED(hr)) {
2108 for (int i = 0; kBrowserProtocolAssociations[i] != nullptr; i++) {
2109 hr = pAAR->SetAppAsDefault(
2110 app_name.c_str(), kBrowserProtocolAssociations[i], AT_URLPROTOCOL);
2111 if (!SUCCEEDED(hr)) {
2112 ret = false;
2113 LOG(ERROR) << "Failed to register as default for protocol "
2114 << kBrowserProtocolAssociations[i] << " (" << hr << ")";
2115 }
2116 }
2117
2118 for (int i = 0; kDefaultFileAssociations[i] != nullptr; i++) {
2119 hr = pAAR->SetAppAsDefault(app_name.c_str(), kDefaultFileAssociations[i],
2120 AT_FILEEXTENSION);
2121 if (!SUCCEEDED(hr)) {
2122 ret = false;
2123 LOG(ERROR) << "Failed to register as default for file extension "
2124 << kDefaultFileAssociations[i] << " (" << hr << ")";
2125 }
2126 }
2127 }
2128
2129 if (!RegisterChromeAsDefaultXPStyle(shell_change, chrome_exe))
2130 ret = false;
2131
2132 // Send Windows notification event so that it can update icons for
2133 // file associations.
2134 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);
2135 return ret;
2136 }
2137
2138 // static
LaunchUninstallAppsSettings()2139 bool ShellUtil::LaunchUninstallAppsSettings() {
2140 DCHECK_GE(base::win::GetVersion(), base::win::Version::WIN10);
2141
2142 static constexpr wchar_t kControlPanelAppModelId[] =
2143 L"windows.immersivecontrolpanel_cw5n1h2txyewy"
2144 L"!microsoft.windows.immersivecontrolpanel";
2145
2146 Microsoft::WRL::ComPtr<IApplicationActivationManager> activator;
2147 HRESULT hr = ::CoCreateInstance(CLSID_ApplicationActivationManager, nullptr,
2148 CLSCTX_ALL, IID_PPV_ARGS(&activator));
2149 if (FAILED(hr))
2150 return false;
2151
2152 DWORD pid = 0;
2153 CoAllowSetForegroundWindow(activator.Get(), nullptr);
2154 hr = activator->ActivateApplication(
2155 kControlPanelAppModelId, L"page=SettingsPageAppsSizes", AO_NONE, &pid);
2156 return SUCCEEDED(hr);
2157 }
2158
ShowMakeChromeDefaultSystemUI(const base::FilePath & chrome_exe)2159 bool ShellUtil::ShowMakeChromeDefaultSystemUI(
2160 const base::FilePath& chrome_exe) {
2161 DCHECK(!CanMakeChromeDefaultUnattended());
2162
2163 if (!install_static::SupportsSetAsDefaultBrowser())
2164 return false;
2165
2166 if (!RegisterChromeBrowser(chrome_exe, base::string16(), true))
2167 return false;
2168
2169 bool succeeded = true;
2170 bool is_default = (GetChromeDefaultState() == IS_DEFAULT);
2171 if (!is_default) {
2172 switch (GetInteractiveSetDefaultMode()) {
2173 case INTENT_PICKER: {
2174 // On Windows 8, you can't set yourself as the default handler
2175 // programmatically. In other words IApplicationAssociationRegistration
2176 // has been rendered useless. What you can do is to launch
2177 // "Set Program Associations" section of the "Default Programs"
2178 // control panel, which is a mess, or pop the concise "How you want to
2179 // open webpages?" dialog. We choose the latter.
2180 ScopedUserProtocolEntry user_protocol_entry(L"http");
2181 succeeded = LaunchSelectDefaultProtocolHandlerDialog(L"http");
2182 } break;
2183 case SYSTEM_SETTINGS:
2184 // On Windows 10, you can't even launch the associations dialog.
2185 // So we launch the settings dialog. Quoting from MSDN: "The Open With
2186 // dialog box can no longer be used to change the default program used
2187 // to open a file extension. You can only use SHOpenWithDialog to open
2188 // a single file."
2189 succeeded = LaunchDefaultAppsSettingsModernDialog(L"http");
2190 break;
2191 }
2192 is_default = (succeeded && GetChromeDefaultState() == IS_DEFAULT);
2193 }
2194 if (succeeded && is_default)
2195 RegisterChromeAsDefaultXPStyle(CURRENT_USER, chrome_exe);
2196 return succeeded;
2197 }
2198
MakeChromeDefaultProtocolClient(const base::FilePath & chrome_exe,const base::string16 & protocol)2199 bool ShellUtil::MakeChromeDefaultProtocolClient(
2200 const base::FilePath& chrome_exe,
2201 const base::string16& protocol) {
2202 if (!install_static::SupportsSetAsDefaultBrowser())
2203 return false;
2204
2205 if (!RegisterChromeForProtocol(chrome_exe, base::string16(), protocol, true))
2206 return false;
2207
2208 // Windows 8 does not permit making a browser default just like that.
2209 // This process needs to be routed through the system's UI. Use
2210 // ShowMakeChromeDefaultProtocolClientSystemUI instead (below).
2211 if (!CanMakeChromeDefaultUnattended())
2212 return false;
2213
2214 bool ret = true;
2215 // First use the "recommended" way introduced in Vista to make Chrome default
2216 // protocol handler.
2217 VLOG(1) << "Registering Chrome as default handler for " << protocol
2218 << " on Windows 7.";
2219 Microsoft::WRL::ComPtr<IApplicationAssociationRegistration> pAAR;
2220 HRESULT hr = ::CoCreateInstance(CLSID_ApplicationAssociationRegistration,
2221 nullptr, CLSCTX_INPROC, IID_PPV_ARGS(&pAAR));
2222 if (SUCCEEDED(hr)) {
2223 base::string16 app_name = GetApplicationName(chrome_exe);
2224 hr = pAAR->SetAppAsDefault(app_name.c_str(), protocol.c_str(),
2225 AT_URLPROTOCOL);
2226 }
2227 if (!SUCCEEDED(hr)) {
2228 ret = false;
2229 LOG(ERROR) << "Could not make Chrome default protocol client (Windows 7):"
2230 << " HRESULT=" << hr << ".";
2231 }
2232
2233 // Now use the old way to associate Chrome with the desired protocol. This
2234 // should not be required on Vista+, but since some applications still read
2235 // Software\Classes\<protocol> key directly, do this on Vista+ also.
2236 if (!RegisterChromeAsDefaultProtocolClientXPStyle(chrome_exe, protocol))
2237 ret = false;
2238
2239 return ret;
2240 }
2241
ShowMakeChromeDefaultProtocolClientSystemUI(const base::FilePath & chrome_exe,const base::string16 & protocol)2242 bool ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(
2243 const base::FilePath& chrome_exe,
2244 const base::string16& protocol) {
2245 DCHECK(!CanMakeChromeDefaultUnattended());
2246
2247 if (!install_static::SupportsSetAsDefaultBrowser())
2248 return false;
2249
2250 if (!RegisterChromeForProtocol(chrome_exe, base::string16(), protocol, true))
2251 return false;
2252
2253 bool succeeded = true;
2254 bool is_default =
2255 (GetChromeDefaultProtocolClientState(protocol) == IS_DEFAULT);
2256 if (!is_default) {
2257 switch (GetInteractiveSetDefaultMode()) {
2258 case INTENT_PICKER: {
2259 // On Windows 8, you can't set yourself as the default handler
2260 // programmatically. In other words IApplicationAssociationRegistration
2261 // has been rendered useless. What you can do is to launch
2262 // "Set Program Associations" section of the "Default Programs"
2263 // control panel, which is a mess, or pop the concise "How you want to
2264 // open
2265 // links of this type (protocol)?" dialog. We choose the latter.
2266 ScopedUserProtocolEntry user_protocol_entry(protocol.c_str());
2267 succeeded = LaunchSelectDefaultProtocolHandlerDialog(protocol.c_str());
2268 } break;
2269 case SYSTEM_SETTINGS:
2270 // On Windows 10, you can't even launch the associations dialog.
2271 // So we launch the settings dialog.
2272 succeeded = LaunchDefaultAppsSettingsModernDialog(protocol.c_str());
2273 break;
2274 }
2275 is_default = (succeeded &&
2276 GetChromeDefaultProtocolClientState(protocol) == IS_DEFAULT);
2277 }
2278 if (succeeded && is_default)
2279 RegisterChromeAsDefaultProtocolClientXPStyle(chrome_exe, protocol);
2280 return succeeded;
2281 }
2282
RegisterChromeBrowser(const base::FilePath & chrome_exe,const base::string16 & unique_suffix,bool elevate_if_not_admin)2283 bool ShellUtil::RegisterChromeBrowser(const base::FilePath& chrome_exe,
2284 const base::string16& unique_suffix,
2285 bool elevate_if_not_admin) {
2286 return RegisterChromeBrowserImpl(chrome_exe, unique_suffix,
2287 elevate_if_not_admin,
2288 /*best_effort_no_rollback=*/false);
2289 }
2290
RegisterChromeBrowserBestEffort(const base::FilePath & chrome_exe)2291 void ShellUtil::RegisterChromeBrowserBestEffort(
2292 const base::FilePath& chrome_exe) {
2293 RegisterChromeBrowserImpl(chrome_exe, base::string16(),
2294 /*elevate_if_not_admin=*/false,
2295 /*best_effort_no_rollback=*/true);
2296 }
2297
RegisterChromeForProtocol(const base::FilePath & chrome_exe,const base::string16 & unique_suffix,const base::string16 & protocol,bool elevate_if_not_admin)2298 bool ShellUtil::RegisterChromeForProtocol(const base::FilePath& chrome_exe,
2299 const base::string16& unique_suffix,
2300 const base::string16& protocol,
2301 bool elevate_if_not_admin) {
2302 base::string16 suffix;
2303 if (!unique_suffix.empty()) {
2304 suffix = unique_suffix;
2305 } else if (!GetInstallationSpecificSuffix(chrome_exe, &suffix)) {
2306 return false;
2307 }
2308
2309 bool user_level = InstallUtil::IsPerUserInstall();
2310 HKEY root = DetermineRegistrationRoot(user_level);
2311
2312 // Look only in HKLM for system-level installs (otherwise, if a user-level
2313 // install is also present, it could lead IsChromeRegisteredForProtocol() to
2314 // think this system-level install isn't registered properly as it may be
2315 // shadowed by the user-level install's registrations).
2316 uint32_t look_for_in = user_level ? RegistryEntry::LOOK_IN_HKCU_THEN_HKLM
2317 : RegistryEntry::LOOK_IN_HKLM;
2318
2319 // Check if chrome is already registered with this suffix.
2320 if (IsChromeRegisteredForProtocol(suffix, protocol, look_for_in))
2321 return true;
2322
2323 if (root == HKEY_CURRENT_USER || IsUserAnAdmin()) {
2324 // We can do this operation directly.
2325 // First, make sure Chrome is fully registered on this machine.
2326 if (!RegisterChromeBrowser(chrome_exe, suffix, false))
2327 return false;
2328
2329 // Write in the capability for the protocol.
2330 std::vector<std::unique_ptr<RegistryEntry>> entries;
2331 GetProtocolCapabilityEntries(suffix, protocol, &entries);
2332 return AddRegistryEntries(root, entries);
2333 } else if (elevate_if_not_admin) {
2334 // Elevate to do the whole job
2335 return ElevateAndRegisterChrome(chrome_exe, suffix, protocol);
2336 } else {
2337 // Admin rights are required to register capabilities before Windows 8.
2338 return false;
2339 }
2340 }
2341
2342 // static
RemoveShortcuts(ShortcutLocation location,ShellChange level,const base::FilePath & target_exe)2343 bool ShellUtil::RemoveShortcuts(ShortcutLocation location,
2344 ShellChange level,
2345 const base::FilePath& target_exe) {
2346 if (!ShortcutLocationIsSupported(location))
2347 return true; // Vacuous success.
2348
2349 FilterTargetEq shortcut_filter(target_exe, false);
2350 // Main operation to apply to each shortcut in the directory specified.
2351 ShortcutOperationCallback shortcut_operation =
2352 location == SHORTCUT_LOCATION_TASKBAR_PINS
2353 ? base::BindRepeating(&ShortcutOpUnpinFromTaskbar)
2354 : base::BindRepeating(&ShortcutOpDelete);
2355 bool success =
2356 BatchShortcutAction(shortcut_filter.AsShortcutFilterCallback(),
2357 shortcut_operation, location, level, nullptr);
2358 // Remove chrome-specific shortcut folders if they are now empty.
2359 if (success &&
2360 (location == SHORTCUT_LOCATION_START_MENU_CHROME_DIR_DEPRECATED ||
2361 location == SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR ||
2362 location == SHORTCUT_LOCATION_APP_SHORTCUTS)) {
2363 success = RemoveShortcutFolderIfEmpty(location, level);
2364 }
2365 return success;
2366 }
2367
2368 // static
RetargetShortcutsWithArgs(ShortcutLocation location,ShellChange level,const base::FilePath & old_target_exe,const base::FilePath & new_target_exe)2369 bool ShellUtil::RetargetShortcutsWithArgs(
2370 ShortcutLocation location,
2371 ShellChange level,
2372 const base::FilePath& old_target_exe,
2373 const base::FilePath& new_target_exe) {
2374 if (!ShortcutLocationIsSupported(location))
2375 return true; // Vacuous success.
2376
2377 FilterTargetEq shortcut_filter(old_target_exe, true);
2378 ShortcutOperationCallback shortcut_operation =
2379 base::BindRepeating(&ShortcutOpRetarget, old_target_exe, new_target_exe);
2380 return BatchShortcutAction(shortcut_filter.AsShortcutFilterCallback(),
2381 shortcut_operation, location, level, nullptr);
2382 }
2383
2384 // static
ShortcutListMaybeRemoveUnknownArgs(ShortcutLocation location,ShellChange level,const base::FilePath & chrome_exe,bool do_removal,const scoped_refptr<SharedCancellationFlag> & cancel,std::vector<std::pair<base::FilePath,base::string16>> * shortcuts)2385 bool ShellUtil::ShortcutListMaybeRemoveUnknownArgs(
2386 ShortcutLocation location,
2387 ShellChange level,
2388 const base::FilePath& chrome_exe,
2389 bool do_removal,
2390 const scoped_refptr<SharedCancellationFlag>& cancel,
2391 std::vector<std::pair<base::FilePath, base::string16>>* shortcuts) {
2392 if (!ShortcutLocationIsSupported(location))
2393 return false;
2394 FilterTargetEq shortcut_filter(chrome_exe, true);
2395 ShortcutOperationCallback shortcut_operation = base::BindRepeating(
2396 &ShortcutOpListOrRemoveUnknownArgs, do_removal, shortcuts);
2397 return BatchShortcutAction(shortcut_filter.AsShortcutFilterCallback(),
2398 shortcut_operation, location, level, cancel);
2399 }
2400
2401 // static
ResetShortcutFileAttributes(ShortcutLocation location,ShellChange level,const base::FilePath & chrome_exe)2402 bool ShellUtil::ResetShortcutFileAttributes(ShortcutLocation location,
2403 ShellChange level,
2404 const base::FilePath& chrome_exe) {
2405 if (!ShortcutLocationIsSupported(location))
2406 return false;
2407 FilterTargetEq shortcut_filter(chrome_exe, /*require_args=*/false);
2408 ShortcutOperationCallback shortcut_operation =
2409 base::BindRepeating(&ShortcutOpResetAttributes);
2410 return BatchShortcutAction(shortcut_filter.AsShortcutFilterCallback(),
2411 shortcut_operation, location, level, nullptr);
2412 }
2413
GetUserSpecificRegistrySuffix(base::string16 * suffix)2414 bool ShellUtil::GetUserSpecificRegistrySuffix(base::string16* suffix) {
2415 // Use a thread-safe cache for the user's suffix.
2416 static base::LazyInstance<UserSpecificRegistrySuffix>::Leaky suffix_instance =
2417 LAZY_INSTANCE_INITIALIZER;
2418 return suffix_instance.Get().GetSuffix(suffix);
2419 }
2420
GetOldUserSpecificRegistrySuffix(base::string16 * suffix)2421 bool ShellUtil::GetOldUserSpecificRegistrySuffix(base::string16* suffix) {
2422 wchar_t user_name[256];
2423 DWORD size = base::size(user_name);
2424 if (::GetUserName(user_name, &size) == 0 || size < 1) {
2425 NOTREACHED();
2426 return false;
2427 }
2428 suffix->reserve(size);
2429 suffix->assign(1, L'.');
2430 suffix->append(user_name, size - 1);
2431 return true;
2432 }
2433
2434 // static
AddFileAssociations(const base::string16 & prog_id,const base::CommandLine & command_line,const base::string16 & application_name,const base::string16 & file_type_name,const base::FilePath & icon_path,const std::set<base::string16> & file_extensions)2435 bool ShellUtil::AddFileAssociations(
2436 const base::string16& prog_id,
2437 const base::CommandLine& command_line,
2438 const base::string16& application_name,
2439 const base::string16& file_type_name,
2440 const base::FilePath& icon_path,
2441 const std::set<base::string16>& file_extensions) {
2442 std::vector<std::unique_ptr<RegistryEntry>> entries;
2443
2444 // Create a class for this app.
2445 ApplicationInfo app_info;
2446 app_info.prog_id = prog_id;
2447 app_info.application_name = application_name;
2448 app_info.application_icon_path = icon_path;
2449 app_info.application_icon_index = 0;
2450 app_info.file_type_name = file_type_name;
2451 app_info.file_type_icon_path = icon_path;
2452 app_info.file_type_icon_index = 0;
2453 app_info.command_line = command_line.GetCommandLineStringForShell();
2454
2455 GetProgIdEntries(app_info, &entries);
2456
2457 std::vector<base::string16> handled_file_extensions;
2458
2459 // Associate each extension that the app can handle with the class. Set this
2460 // app as the default handler if and only if there is no existing default.
2461 for (const auto& file_extension : file_extensions) {
2462 // Do not allow empty file extensions, or extensions beginning with a '.'.
2463 DCHECK(!file_extension.empty());
2464 DCHECK_NE(L'.', file_extension[0]);
2465 base::string16 ext(1, L'.');
2466 ext.append(file_extension);
2467 GetAppExtRegistrationEntries(prog_id, ext, &entries);
2468
2469 // Registering as the default will have no effect on Windows 8 (see
2470 // documentation for GetAppDefaultRegistrationEntries). However, if our app
2471 // is the only handler, it will automatically become the default, so the
2472 // same effect is achieved.
2473 GetAppDefaultRegistrationEntries(prog_id, ext, false, &entries);
2474
2475 handled_file_extensions.push_back(std::move(ext));
2476 }
2477
2478 // Save handled file extensions in the registry for use during uninstallation.
2479 base::string16 prog_id_path(ShellUtil::kRegClasses);
2480 prog_id_path.push_back(base::FilePath::kSeparators[0]);
2481 prog_id_path.append(prog_id);
2482 entries.push_back(std::make_unique<RegistryEntry>(
2483 prog_id_path, L"FileExtensions",
2484 base::JoinString(handled_file_extensions, L";")));
2485
2486 return AddRegistryEntries(HKEY_CURRENT_USER, entries);
2487 }
2488
2489 // static
DeleteFileAssociations(const base::string16 & prog_id)2490 bool ShellUtil::DeleteFileAssociations(const base::string16& prog_id) {
2491 base::string16 prog_id_path(kRegClasses);
2492 prog_id_path.push_back(base::FilePath::kSeparators[0]);
2493 prog_id_path.append(prog_id);
2494
2495 // Get list of handled file extensions from value FileExtensions at
2496 // HKEY_CURRENT_USER\Software\Classes\|prog_id|.
2497 RegKey file_extensions_key(HKEY_CURRENT_USER, prog_id_path.c_str(),
2498 KEY_QUERY_VALUE);
2499 base::string16 handled_file_extensions;
2500 if (file_extensions_key.ReadValue(
2501 L"FileExtensions", &handled_file_extensions) == ERROR_SUCCESS) {
2502 std::vector<base::string16> file_extensions =
2503 base::SplitString(handled_file_extensions, base::string16(L";"),
2504 base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
2505
2506 // Delete file-extension-handling registry entries for each file extension.
2507 for (const auto& file_extension : file_extensions) {
2508 base::string16 extension_path(kRegClasses);
2509 extension_path.push_back(base::FilePath::kSeparators[0]);
2510 extension_path.append(file_extension);
2511
2512 // Delete the default value at
2513 // HKEY_CURRENT_USER\Software\Classes\.<extension> if set to |prog_id|;
2514 // this unregisters |prog_id| as the default handler for |file_extension|.
2515 InstallUtil::DeleteRegistryValueIf(
2516 HKEY_CURRENT_USER, extension_path.c_str(), WorkItem::kWow64Default,
2517 L"", InstallUtil::ValueEquals(prog_id));
2518
2519 // Delete value |prog_id| at
2520 // HKEY_CURRENT_USER\Software\Classes\.<extension>\OpenWithProgids;
2521 // this removes |prog_id| from the list of handlers for |file_extension|.
2522 extension_path.push_back(base::FilePath::kSeparators[0]);
2523 extension_path.append(ShellUtil::kRegOpenWithProgids);
2524 InstallUtil::DeleteRegistryValue(HKEY_CURRENT_USER, extension_path,
2525 WorkItem::kWow64Default, prog_id);
2526 }
2527 }
2528
2529 // Delete the key HKEY_CURRENT_USER\Software\Classes\|prog_id|.
2530 return InstallUtil::DeleteRegistryKey(HKEY_CURRENT_USER, prog_id_path,
2531 WorkItem::kWow64Default);
2532 }
2533
2534 // static
AddApplicationClass(const base::string16 & prog_id,const base::CommandLine & shell_open_command_line,const base::string16 & application_name,const base::string16 & application_description,const base::FilePath & icon_path)2535 bool ShellUtil::AddApplicationClass(
2536 const base::string16& prog_id,
2537 const base::CommandLine& shell_open_command_line,
2538 const base::string16& application_name,
2539 const base::string16& application_description,
2540 const base::FilePath& icon_path) {
2541 ApplicationInfo app_info;
2542
2543 app_info.prog_id = prog_id;
2544 app_info.file_type_name = application_description;
2545 app_info.file_type_icon_path = icon_path;
2546 app_info.command_line =
2547 shell_open_command_line.GetCommandLineStringForShell();
2548 app_info.application_name = application_name;
2549 app_info.application_icon_path = icon_path;
2550 app_info.application_icon_index = 0;
2551
2552 std::vector<std::unique_ptr<RegistryEntry>> entries;
2553 GetProgIdEntries(app_info, &entries);
2554
2555 return AreEntriesAsDesired(entries, RegistryEntry::LOOK_IN_HKCU) ||
2556 AddRegistryEntries(HKEY_CURRENT_USER, entries);
2557 }
2558
2559 // static
DeleteApplicationClass(const base::string16 & prog_id)2560 bool ShellUtil::DeleteApplicationClass(const base::string16& prog_id) {
2561 base::string16 prog_id_path(kRegClasses);
2562 prog_id_path.push_back(base::FilePath::kSeparators[0]);
2563 prog_id_path.append(prog_id);
2564
2565 // Delete the key HKEY_CURRENT_USER\Software\Classes\|prog_id|.
2566 return InstallUtil::DeleteRegistryKey(HKEY_CURRENT_USER, prog_id_path,
2567 WorkItem::kWow64Default);
2568 }
2569
2570 // static
GetFileAssociationsAndAppName(const base::string16 & prog_id)2571 ShellUtil::FileAssociationsAndAppName ShellUtil::GetFileAssociationsAndAppName(
2572 const base::string16& prog_id) {
2573 FileAssociationsAndAppName file_associations_and_app_name;
2574
2575 base::string16 prog_id_path(kRegClasses);
2576 prog_id_path.push_back(base::FilePath::kSeparators[0]);
2577 prog_id_path.append(prog_id);
2578
2579 // Get the app name from value ApplicationName at
2580 // HKEY_CURRENT_USER\Software\Classes\|prog_id|\Application.
2581 base::string16 application_path = prog_id_path + kRegApplication;
2582 RegKey application_key(HKEY_CURRENT_USER, application_path.c_str(),
2583 KEY_QUERY_VALUE);
2584 if (application_key.ReadValue(kRegApplicationName,
2585 &file_associations_and_app_name.app_name) !=
2586 ERROR_SUCCESS) {
2587 return file_associations_and_app_name;
2588 }
2589
2590 // If present, Get list of handled file extensions from value FileExtensions
2591 // at HKEY_CURRENT_USER\Software\Classes\|prog_id|.
2592 RegKey file_extensions_key(HKEY_CURRENT_USER, prog_id_path.c_str(),
2593 KEY_QUERY_VALUE);
2594 base::string16 handled_file_extensions;
2595 if (file_extensions_key.ReadValue(
2596 L"FileExtensions", &handled_file_extensions) == ERROR_SUCCESS) {
2597 std::vector<base::StringPiece16> file_associations_vec =
2598 base::SplitStringPiece(base::StringPiece16(handled_file_extensions),
2599 base::StringPiece16(STRING16_LITERAL(";")),
2600 base::TRIM_WHITESPACE,
2601 base::SPLIT_WANT_NONEMPTY);
2602 for (const auto& file_extension : file_associations_vec) {
2603 // Skip over the leading '.' so that we return the same
2604 // extensions as were passed to AddFileAssociations.
2605 file_associations_and_app_name.file_associations.emplace(
2606 file_extension.substr(1));
2607 }
2608 }
2609 return file_associations_and_app_name;
2610 }
2611
2612 // static
GetApplicationPathForProgId(const base::string16 & prog_id)2613 base::FilePath ShellUtil::GetApplicationPathForProgId(
2614 const base::string16& prog_id) {
2615 base::string16 prog_id_path(kRegClasses);
2616 prog_id_path.push_back(base::FilePath::kSeparators[0]);
2617 prog_id_path.append(prog_id);
2618 prog_id_path.append(kRegShellOpen);
2619 base::string16 command_line;
2620 RegKey command_line_key(HKEY_CURRENT_USER, prog_id_path.c_str(),
2621 KEY_QUERY_VALUE);
2622 if (command_line_key.ReadValue(L"", &command_line) == ERROR_SUCCESS)
2623 return base::CommandLine::FromString(command_line).GetProgram();
2624
2625 return base::FilePath();
2626 }
2627
2628 // static
AddRegistryEntries(HKEY root,const std::vector<std::unique_ptr<RegistryEntry>> & entries,bool best_effort_no_rollback)2629 bool ShellUtil::AddRegistryEntries(
2630 HKEY root,
2631 const std::vector<std::unique_ptr<RegistryEntry>>& entries,
2632 bool best_effort_no_rollback) {
2633 std::unique_ptr<WorkItemList> items(WorkItem::CreateWorkItemList());
2634 items->set_rollback_enabled(!best_effort_no_rollback);
2635 items->set_best_effort(best_effort_no_rollback);
2636 for (const auto& entry : entries)
2637 entry->AddToWorkItemList(root, items.get());
2638
2639 // Apply all the registry changes and if there is a problem, rollback
2640 if (!items->Do()) {
2641 items->Rollback();
2642 return false;
2643 }
2644 return true;
2645 }
2646