OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 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 #include "chrome/installer/util/shell_registry_util.h" |
| 6 |
| 7 #include "base/memory/scoped_ptr.h" |
| 8 #include "base/strings/utf_string_conversions.h" |
| 9 #include "base/win/windows_version.h" |
| 10 #include "chrome/common/chrome_switches.h" |
| 11 #include "chrome/installer/util/browser_distribution.h" |
| 12 #include "chrome/installer/util/install_util.h" |
| 13 #include "chrome/installer/util/installer_util_strings.h" |
| 14 #include "chrome/installer/util/l10n_string_util.h" |
| 15 #include "chrome/installer/util/registry_entry.h" |
| 16 #include "chrome/installer/util/shell_util.h" |
| 17 |
| 18 namespace installer_util { |
| 19 |
| 20 namespace { |
| 21 |
| 22 // Returns the current (or installed) browser's ProgId (e.g. |
| 23 // "ChromeHTML|suffix|"). |
| 24 // |suffix| can be the empty string. |
| 25 base::string16 GetBrowserProgId(const base::string16& suffix) { |
| 26 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
| 27 base::string16 chrome_html(dist->GetBrowserProgIdPrefix()); |
| 28 chrome_html.append(suffix); |
| 29 |
| 30 // ProgIds cannot be longer than 39 characters. |
| 31 // Ref: http://msdn.microsoft.com/en-us/library/aa911706.aspx. |
| 32 // Make all new registrations comply with this requirement (existing |
| 33 // registrations must be preserved). |
| 34 base::string16 new_style_suffix; |
| 35 if (ShellUtil::GetUserSpecificRegistrySuffix(&new_style_suffix) && |
| 36 suffix == new_style_suffix && chrome_html.length() > 39) { |
| 37 NOTREACHED(); |
| 38 chrome_html.erase(39); |
| 39 } |
| 40 return chrome_html; |
| 41 } |
| 42 |
| 43 // Returns the Windows Default Programs capabilities key for Chrome. For |
| 44 // example: |
| 45 // "Software\Clients\StartMenuInternet\Chromium[.user]\Capabilities". |
| 46 base::string16 GetCapabilitiesKey(BrowserDistribution* dist, |
| 47 const base::string16& suffix) { |
| 48 return GetBrowserClientKey(dist, suffix).append(L"\\Capabilities"); |
| 49 } |
| 50 |
| 51 // DelegateExecute ProgId. Needed for Chrome Metro in Windows 8. This is only |
| 52 // needed for registring a web browser, not for general associations. |
| 53 ScopedVector<RegistryEntry> GetChromeDelegateExecuteEntries( |
| 54 const base::FilePath& chrome_exe, |
| 55 const ApplicationInfo& app_info) { |
| 56 ScopedVector<RegistryEntry> entries; |
| 57 |
| 58 base::string16 app_id_shell_key(ShellUtil::kRegClasses); |
| 59 app_id_shell_key.push_back(base::FilePath::kSeparators[0]); |
| 60 app_id_shell_key.append(app_info.app_id); |
| 61 app_id_shell_key.append(ShellUtil::kRegExePath); |
| 62 app_id_shell_key.append(ShellUtil::kRegShellPath); |
| 63 |
| 64 // <root hkey>\Software\Classes\<app_id>\.exe\shell @=open |
| 65 entries.push_back( |
| 66 new RegistryEntry(app_id_shell_key, ShellUtil::kRegVerbOpen)); |
| 67 |
| 68 // The command to execute when opening this application via the Metro UI. |
| 69 const base::string16 delegate_command( |
| 70 ShellUtil::GetChromeDelegateCommand(chrome_exe)); |
| 71 |
| 72 // Each of Chrome's shortcuts has an appid; which, as of Windows 8, is |
| 73 // registered to handle some verbs. This registration has the side-effect |
| 74 // that these verbs now show up in the shortcut's context menu. We |
| 75 // mitigate this side-effect by making the context menu entries |
| 76 // user readable/localized strings. See relevant MSDN article: |
| 77 // http://msdn.microsoft.com/en-US/library/windows/desktop/cc144171.aspx |
| 78 static const struct { |
| 79 const wchar_t* verb; |
| 80 int name_id; |
| 81 } verbs[] = { |
| 82 {ShellUtil::kRegVerbOpen, -1}, |
| 83 {ShellUtil::kRegVerbOpenNewWindow, IDS_SHORTCUT_NEW_WINDOW_BASE}, |
| 84 }; |
| 85 for (const auto& verb_and_id : verbs) { |
| 86 base::string16 sub_path(app_id_shell_key); |
| 87 sub_path.push_back(base::FilePath::kSeparators[0]); |
| 88 sub_path.append(verb_and_id.verb); |
| 89 |
| 90 // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb> |
| 91 if (verb_and_id.name_id != -1) { |
| 92 // TODO(grt): http://crbug.com/75152 Write a reference to a localized |
| 93 // resource. |
| 94 const base::string16 verb_name( |
| 95 installer::GetLocalizedString(verb_and_id.name_id)); |
| 96 entries.push_back(new RegistryEntry(sub_path, verb_name.c_str())); |
| 97 } |
| 98 entries.push_back( |
| 99 new RegistryEntry(sub_path, L"CommandId", L"Browser.Launch")); |
| 100 |
| 101 sub_path.push_back(base::FilePath::kSeparators[0]); |
| 102 sub_path.append(ShellUtil::kRegCommand); |
| 103 |
| 104 // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb>\command |
| 105 entries.push_back(new RegistryEntry(sub_path, delegate_command)); |
| 106 entries.push_back(new RegistryEntry( |
| 107 sub_path, ShellUtil::kRegDelegateExecute, app_info.delegate_clsid)); |
| 108 } |
| 109 |
| 110 return entries; |
| 111 } |
| 112 |
| 113 } // namespace |
| 114 |
| 115 const wchar_t kReinstallCommand[] = L"ReinstallCommand"; |
| 116 |
| 117 base::string16 GetBrowserClientKey(BrowserDistribution* dist, |
| 118 const base::string16& suffix) { |
| 119 DCHECK(suffix.empty() || suffix[0] == L'.'); |
| 120 return base::string16(ShellUtil::kRegStartMenuInternet) |
| 121 .append(1, L'\\') |
| 122 .append(dist->GetBaseAppName()) |
| 123 .append(suffix); |
| 124 } |
| 125 |
| 126 void GetProgIdEntries(const ApplicationInfo& app_info, |
| 127 ScopedVector<RegistryEntry>* entries) { |
| 128 // Basic sanity checks. |
| 129 DCHECK(!app_info.prog_id.empty()); |
| 130 DCHECK_NE(L'.', app_info.prog_id[0]); |
| 131 |
| 132 // File association ProgId |
| 133 base::string16 prog_id_path(ShellUtil::kRegClasses); |
| 134 prog_id_path.push_back(base::FilePath::kSeparators[0]); |
| 135 prog_id_path.append(app_info.prog_id); |
| 136 entries->push_back(new RegistryEntry(prog_id_path, app_info.file_type_name)); |
| 137 entries->push_back(new RegistryEntry( |
| 138 prog_id_path + ShellUtil::kRegDefaultIcon, |
| 139 ShellUtil::FormatIconLocation(app_info.file_type_icon_path, |
| 140 app_info.file_type_icon_index))); |
| 141 entries->push_back(new RegistryEntry(prog_id_path + ShellUtil::kRegShellOpen, |
| 142 app_info.command_line)); |
| 143 if (!app_info.delegate_clsid.empty()) { |
| 144 entries->push_back(new RegistryEntry( |
| 145 prog_id_path + ShellUtil::kRegShellOpen, ShellUtil::kRegDelegateExecute, |
| 146 app_info.delegate_clsid)); |
| 147 // TODO(scottmg): Simplify after Metro removal. https://crbug.com/558054. |
| 148 entries->back()->set_removal_flag(RegistryEntry::RemovalFlag::VALUE); |
| 149 } |
| 150 |
| 151 // The following entries are required as of Windows 8, but do not |
| 152 // depend on the DelegateExecute verb handler being set. |
| 153 if (base::win::GetVersion() >= base::win::VERSION_WIN8) { |
| 154 if (!app_info.app_id.empty()) { |
| 155 entries->push_back(new RegistryEntry( |
| 156 prog_id_path, ShellUtil::kRegAppUserModelId, app_info.app_id)); |
| 157 } |
| 158 |
| 159 // Add \Software\Classes\<prog_id>\Application entries |
| 160 base::string16 application_path(prog_id_path + ShellUtil::kRegApplication); |
| 161 if (!app_info.app_id.empty()) { |
| 162 entries->push_back(new RegistryEntry( |
| 163 application_path, ShellUtil::kRegAppUserModelId, app_info.app_id)); |
| 164 } |
| 165 if (!app_info.application_icon_path.empty()) { |
| 166 entries->push_back(new RegistryEntry( |
| 167 application_path, ShellUtil::kRegApplicationIcon, |
| 168 ShellUtil::FormatIconLocation(app_info.application_icon_path, |
| 169 app_info.application_icon_index))); |
| 170 } |
| 171 if (!app_info.application_name.empty()) { |
| 172 entries->push_back(new RegistryEntry(application_path, |
| 173 ShellUtil::kRegApplicationName, |
| 174 app_info.application_name)); |
| 175 } |
| 176 if (!app_info.application_description.empty()) { |
| 177 entries->push_back(new RegistryEntry( |
| 178 application_path, ShellUtil::kRegApplicationDescription, |
| 179 app_info.application_description)); |
| 180 } |
| 181 if (!app_info.publisher_name.empty()) { |
| 182 entries->push_back(new RegistryEntry(application_path, |
| 183 ShellUtil::kRegApplicationCompany, |
| 184 app_info.publisher_name)); |
| 185 } |
| 186 } |
| 187 } |
| 188 |
| 189 void GetChromeProgIdEntries(BrowserDistribution* dist, |
| 190 const base::FilePath& chrome_exe, |
| 191 const base::string16& suffix, |
| 192 ScopedVector<RegistryEntry>* entries) { |
| 193 int chrome_icon_index = |
| 194 dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME); |
| 195 |
| 196 ApplicationInfo app_info; |
| 197 app_info.prog_id = GetBrowserProgId(suffix); |
| 198 app_info.file_type_name = dist->GetBrowserProgIdDesc(); |
| 199 // File types associated with Chrome are just given the Chrome icon. |
| 200 app_info.file_type_icon_path = chrome_exe; |
| 201 app_info.file_type_icon_index = chrome_icon_index; |
| 202 app_info.command_line = ShellUtil::GetChromeShellOpenCmd(chrome_exe); |
| 203 // For user-level installs: entries for the app id will be in HKCU; thus we |
| 204 // do not need a suffix on those entries. |
| 205 app_info.app_id = ShellUtil::GetBrowserModelId( |
| 206 dist, InstallUtil::IsPerUserInstall(chrome_exe)); |
| 207 |
| 208 // TODO(grt): http://crbug.com/75152 Write a reference to a localized |
| 209 // resource for name, description, and company. |
| 210 app_info.application_name = dist->GetDisplayName(); |
| 211 app_info.application_icon_path = chrome_exe; |
| 212 app_info.application_icon_index = chrome_icon_index; |
| 213 app_info.application_description = dist->GetAppDescription(); |
| 214 app_info.publisher_name = dist->GetPublisherName(); |
| 215 |
| 216 app_info.delegate_clsid = dist->GetCommandExecuteImplClsid(); |
| 217 |
| 218 GetProgIdEntries(app_info, entries); |
| 219 |
| 220 if (!app_info.delegate_clsid.empty()) { |
| 221 ScopedVector<RegistryEntry> delegate_execute_entries = |
| 222 GetChromeDelegateExecuteEntries(chrome_exe, app_info); |
| 223 // Remove the keys (not only their values) so that Windows will continue |
| 224 // to launch Chrome without a pesky association error. |
| 225 // TODO(scottmg): Simplify after Metro removal. https://crbug.com/558054. |
| 226 for (RegistryEntry* entry : delegate_execute_entries) |
| 227 entry->set_removal_flag(RegistryEntry::RemovalFlag::KEY); |
| 228 // Move |delegate_execute_entries| to |entries|. |
| 229 entries->insert(entries->end(), delegate_execute_entries.begin(), |
| 230 delegate_execute_entries.end()); |
| 231 delegate_execute_entries.weak_clear(); |
| 232 } |
| 233 } |
| 234 |
| 235 void GetProtocolCapabilityEntries(BrowserDistribution* dist, |
| 236 const base::string16& suffix, |
| 237 const base::string16& protocol, |
| 238 ScopedVector<RegistryEntry>* entries) { |
| 239 entries->push_back(new RegistryEntry( |
| 240 GetCapabilitiesKey(dist, suffix).append(L"\\URLAssociations"), protocol, |
| 241 GetBrowserProgId(suffix))); |
| 242 } |
| 243 |
| 244 void GetShellIntegrationEntries(BrowserDistribution* dist, |
| 245 const base::FilePath& chrome_exe, |
| 246 const base::string16& suffix, |
| 247 ScopedVector<RegistryEntry>* entries) { |
| 248 const base::string16 icon_path(ShellUtil::FormatIconLocation( |
| 249 chrome_exe, dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME))); |
| 250 const base::string16 quoted_exe_path(L"\"" + chrome_exe.value() + L"\""); |
| 251 |
| 252 // Register for the Start Menu "Internet" link (pre-Win7). |
| 253 const base::string16 start_menu_entry(GetBrowserClientKey(dist, suffix)); |
| 254 // Register Chrome's display name. |
| 255 // TODO(grt): http://crbug.com/75152 Also set LocalizedString; see |
| 256 // http://msdn.microsoft.com/en-us/library/windows/desktop/cc144109(v=VS.85).a
spx#registering_the_display_name |
| 257 entries->push_back( |
| 258 new RegistryEntry(start_menu_entry, dist->GetDisplayName())); |
| 259 // Register the "open" verb for launching Chrome via the "Internet" link. |
| 260 entries->push_back(new RegistryEntry( |
| 261 start_menu_entry + ShellUtil::kRegShellOpen, quoted_exe_path)); |
| 262 // Register Chrome's icon for the Start Menu "Internet" link. |
| 263 entries->push_back(new RegistryEntry( |
| 264 start_menu_entry + ShellUtil::kRegDefaultIcon, icon_path)); |
| 265 |
| 266 // Register installation information. |
| 267 base::string16 install_info(start_menu_entry + L"\\InstallInfo"); |
| 268 // Note: not using CommandLine since it has ambiguous rules for quoting |
| 269 // strings. |
| 270 entries->push_back( |
| 271 new RegistryEntry(install_info, kReinstallCommand, |
| 272 quoted_exe_path + L" --" + |
| 273 base::ASCIIToUTF16(switches::kMakeDefaultBrowser))); |
| 274 entries->push_back(new RegistryEntry( |
| 275 install_info, L"HideIconsCommand", |
| 276 quoted_exe_path + L" --" + base::ASCIIToUTF16(switches::kHideIcons))); |
| 277 entries->push_back(new RegistryEntry( |
| 278 install_info, L"ShowIconsCommand", |
| 279 quoted_exe_path + L" --" + base::ASCIIToUTF16(switches::kShowIcons))); |
| 280 entries->push_back(new RegistryEntry(install_info, L"IconsVisible", 1)); |
| 281 |
| 282 // Register with Default Programs. |
| 283 const base::string16 reg_app_name(dist->GetBaseAppName().append(suffix)); |
| 284 // Tell Windows where to find Chrome's Default Programs info. |
| 285 const base::string16 capabilities(GetCapabilitiesKey(dist, suffix)); |
| 286 entries->push_back(new RegistryEntry(ShellUtil::kRegRegisteredApplications, |
| 287 reg_app_name, capabilities)); |
| 288 // Write out Chrome's Default Programs info. |
| 289 // TODO(grt): http://crbug.com/75152 Write a reference to a localized |
| 290 // resource rather than this. |
| 291 entries->push_back(new RegistryEntry(capabilities, |
| 292 ShellUtil::kRegApplicationDescription, |
| 293 dist->GetLongAppDescription())); |
| 294 entries->push_back(new RegistryEntry( |
| 295 capabilities, ShellUtil::kRegApplicationIcon, icon_path)); |
| 296 entries->push_back(new RegistryEntry( |
| 297 capabilities, ShellUtil::kRegApplicationName, dist->GetDisplayName())); |
| 298 |
| 299 entries->push_back(new RegistryEntry(capabilities + L"\\Startmenu", |
| 300 L"StartMenuInternet", reg_app_name)); |
| 301 |
| 302 const base::string16 html_prog_id(GetBrowserProgId(suffix)); |
| 303 for (int i = 0; ShellUtil::kPotentialFileAssociations[i] != NULL; i++) { |
| 304 entries->push_back(new RegistryEntry( |
| 305 capabilities + L"\\FileAssociations", |
| 306 ShellUtil::kPotentialFileAssociations[i], html_prog_id)); |
| 307 } |
| 308 for (int i = 0; ShellUtil::kPotentialProtocolAssociations[i] != NULL; i++) { |
| 309 entries->push_back(new RegistryEntry( |
| 310 capabilities + L"\\URLAssociations", |
| 311 ShellUtil::kPotentialProtocolAssociations[i], html_prog_id)); |
| 312 } |
| 313 } |
| 314 |
| 315 void GetAppExtRegistrationEntries(const base::string16& prog_id, |
| 316 const base::string16& ext, |
| 317 ScopedVector<RegistryEntry>* entries) { |
| 318 // In HKEY_CURRENT_USER\Software\Classes\EXT\OpenWithProgids, create an |
| 319 // empty value with this class's ProgId. |
| 320 base::string16 key_name(ShellUtil::kRegClasses); |
| 321 key_name.push_back(base::FilePath::kSeparators[0]); |
| 322 key_name.append(ext); |
| 323 key_name.push_back(base::FilePath::kSeparators[0]); |
| 324 key_name.append(ShellUtil::kRegOpenWithProgids); |
| 325 entries->push_back(new RegistryEntry(key_name, prog_id, base::string16())); |
| 326 } |
| 327 |
| 328 void GetChromeAppRegistrationEntries(const base::FilePath& chrome_exe, |
| 329 const base::string16& suffix, |
| 330 ScopedVector<RegistryEntry>* entries) { |
| 331 base::string16 app_path_key(ShellUtil::kAppPathsRegistryKey); |
| 332 app_path_key.push_back(base::FilePath::kSeparators[0]); |
| 333 app_path_key.append(chrome_exe.BaseName().value()); |
| 334 entries->push_back(new RegistryEntry(app_path_key, chrome_exe.value())); |
| 335 entries->push_back(new RegistryEntry(app_path_key, |
| 336 ShellUtil::kAppPathsRegistryPathName, |
| 337 chrome_exe.DirName().value())); |
| 338 |
| 339 const base::string16 html_prog_id(GetBrowserProgId(suffix)); |
| 340 for (int i = 0; ShellUtil::kPotentialFileAssociations[i] != NULL; i++) { |
| 341 GetAppExtRegistrationEntries( |
| 342 html_prog_id, ShellUtil::kPotentialFileAssociations[i], entries); |
| 343 } |
| 344 } |
| 345 |
| 346 void GetAppDefaultRegistrationEntries(const base::string16& prog_id, |
| 347 const base::string16& ext, |
| 348 bool overwrite_existing, |
| 349 ScopedVector<RegistryEntry>* entries) { |
| 350 // Set the default value of HKEY_CURRENT_USER\Software\Classes\EXT to this |
| 351 // class's name. |
| 352 base::string16 key_name(ShellUtil::kRegClasses); |
| 353 key_name.push_back(base::FilePath::kSeparators[0]); |
| 354 key_name.append(ext); |
| 355 scoped_ptr<RegistryEntry> default_association( |
| 356 new RegistryEntry(key_name, prog_id)); |
| 357 if (overwrite_existing || |
| 358 !default_association->KeyExistsInRegistry(RegistryEntry::LOOK_IN_HKCU)) { |
| 359 entries->push_back(default_association.release()); |
| 360 } |
| 361 } |
| 362 |
| 363 void GetXPStyleUserProtocolEntries(const base::string16& protocol, |
| 364 const base::string16& chrome_icon, |
| 365 const base::string16& chrome_open, |
| 366 ScopedVector<RegistryEntry>* entries) { |
| 367 // Protocols associations. |
| 368 base::string16 url_key(ShellUtil::kRegClasses); |
| 369 url_key.push_back(base::FilePath::kSeparators[0]); |
| 370 url_key.append(protocol); |
| 371 |
| 372 // This registry value tells Windows that this 'class' is a URL scheme |
| 373 // so IE, explorer and other apps will route it to our handler. |
| 374 // <root hkey>\Software\Classes\<protocol>\URL Protocol |
| 375 entries->push_back( |
| 376 new RegistryEntry(url_key, ShellUtil::kRegUrlProtocol, base::string16())); |
| 377 |
| 378 // <root hkey>\Software\Classes\<protocol>\DefaultIcon |
| 379 base::string16 icon_key = url_key + ShellUtil::kRegDefaultIcon; |
| 380 entries->push_back(new RegistryEntry(icon_key, chrome_icon)); |
| 381 |
| 382 // <root hkey>\Software\Classes\<protocol>\shell\open\command |
| 383 base::string16 shell_key = url_key + ShellUtil::kRegShellOpen; |
| 384 entries->push_back(new RegistryEntry(shell_key, chrome_open)); |
| 385 |
| 386 // <root hkey>\Software\Classes\<protocol>\shell\open\ddeexec |
| 387 base::string16 dde_key = url_key + L"\\shell\\open\\ddeexec"; |
| 388 entries->push_back(new RegistryEntry(dde_key, base::string16())); |
| 389 |
| 390 // <root hkey>\Software\Classes\<protocol>\shell\@ |
| 391 base::string16 protocol_shell_key = url_key + ShellUtil::kRegShellPath; |
| 392 entries->push_back(new RegistryEntry(protocol_shell_key, L"open")); |
| 393 } |
| 394 |
| 395 void GetXPStyleDefaultBrowserUserEntries(BrowserDistribution* dist, |
| 396 const base::FilePath& chrome_exe, |
| 397 const base::string16& suffix, |
| 398 ScopedVector<RegistryEntry>* entries) { |
| 399 // File extension associations. |
| 400 base::string16 html_prog_id(GetBrowserProgId(suffix)); |
| 401 for (int i = 0; ShellUtil::kDefaultFileAssociations[i] != NULL; i++) { |
| 402 GetAppDefaultRegistrationEntries( |
| 403 html_prog_id, ShellUtil::kDefaultFileAssociations[i], true, entries); |
| 404 } |
| 405 |
| 406 // Protocols associations. |
| 407 base::string16 chrome_open = ShellUtil::GetChromeShellOpenCmd(chrome_exe); |
| 408 base::string16 chrome_icon = ShellUtil::FormatIconLocation( |
| 409 chrome_exe, dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME)); |
| 410 for (int i = 0; ShellUtil::kBrowserProtocolAssociations[i] != NULL; i++) { |
| 411 GetXPStyleUserProtocolEntries(ShellUtil::kBrowserProtocolAssociations[i], |
| 412 chrome_icon, chrome_open, entries); |
| 413 } |
| 414 |
| 415 // start->Internet shortcut. |
| 416 base::string16 start_menu(ShellUtil::kRegStartMenuInternet); |
| 417 base::string16 app_name = dist->GetBaseAppName() + suffix; |
| 418 entries->push_back(new RegistryEntry(start_menu, app_name)); |
| 419 } |
| 420 |
| 421 } // namespace installer_util |
OLD | NEW |