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(&registration));
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, &current_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, &current_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(&registration));
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                      [&registration, 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