Chromium Code Reviews| Index: chrome/installer/util/shell_util.cc |
| diff --git a/chrome/installer/util/shell_util.cc b/chrome/installer/util/shell_util.cc |
| index 202d8716381ad6b28dd97721e94f13e3d46e48ef..87346d024ac248d2ff947df805313f767143dbf5 100644 |
| --- a/chrome/installer/util/shell_util.cc |
| +++ b/chrome/installer/util/shell_util.cc |
| @@ -80,6 +80,8 @@ enum RegistrationConfirmationLevel { |
| }; |
| const wchar_t kReinstallCommand[] = L"ReinstallCommand"; |
| +const wchar_t kSystemSettingsDefaultAppsFormat[] = |
| + L"SystemSettings_DefaultApps_%ls"; |
| // Returns the current (or installed) browser's ProgId (e.g. |
| // "ChromeHTML|suffix|"). |
| @@ -709,24 +711,29 @@ bool ElevateAndRegisterChrome(BrowserDistribution* dist, |
| // Launches the Windows 'settings' modern app with the 'default apps' view |
| // focused. This only works for Windows 8 and Windows 10. The appModelId |
| -// looks arbitrary but it is the same in Win8 and Win10 previews. There |
| -// is no easy way to retrieve the appModelId from the registry. |
| -bool LaunchDefaultAppsSettingsModernDialog() { |
| +// looks arbitrary but it is the same in Win8 and Win10. There is no easy way to |
| +// retrieve the appModelId from the registry. |
| +bool LaunchDefaultAppsSettingsModernDialog(const wchar_t* target) { |
|
grt (UTC plus 2)
2016/04/15 18:47:26
how about having this take the protocol rather tha
Patrick Monette
2016/04/15 21:35:42
Done.
|
| + DCHECK(target); |
| + static const wchar_t kControlPanelAppModelId[] = |
| + L"windows.immersivecontrolpanel_cw5n1h2txyewy" |
| + L"!microsoft.windows.immersivecontrolpanel"; |
| + |
| base::win::ScopedComPtr<IApplicationActivationManager> activator; |
| HRESULT hr = activator.CreateInstance(CLSID_ApplicationActivationManager); |
| if (SUCCEEDED(hr)) { |
| DWORD pid = 0; |
| CoAllowSetForegroundWindow(activator.get(), nullptr); |
| - hr = activator->ActivateApplication( |
| - L"windows.immersivecontrolpanel_cw5n1h2txyewy" |
| - L"!microsoft.windows.immersivecontrolpanel", |
| - L"page=SettingsPageAppsDefaults", AO_NONE, &pid); |
| + hr = activator->ActivateApplication(kControlPanelAppModelId, |
| + L"page=SettingsPageAppsDefaults", |
| + AO_NONE, &pid); |
| if (SUCCEEDED(hr)) { |
| hr = activator->ActivateApplication( |
| - L"windows.immersivecontrolpanel_cw5n1h2txyewy" |
| - L"!microsoft.windows.immersivecontrolpanel", |
| - L"page=SettingsPageAppsDefaults" |
| - L"&target=SystemSettings_DefaultApps_Browser", AO_NONE, &pid); |
| + kControlPanelAppModelId, |
| + base::StringPrintf(L"page=SettingsPageAppsDefaults&target=%ls", |
| + target) |
| + .c_str(), |
| + AO_NONE, &pid); |
| } |
| if (SUCCEEDED(hr)) |
| return true; |
| @@ -1343,6 +1350,28 @@ bool RemoveShortcutFolderIfEmpty(ShellUtil::ShortcutLocation location, |
| return true; |
| } |
| +// Returns the target used as a activate parameter when opening the settings |
| +// pointing to the page that is the most relevant to a user trying to change the |
| +// default handler for |protocol|. |
| +base::string16 GetTargetForDefaultAppsSettings(const base::string16& protocol) { |
| + if (protocol == L"mailto") |
| + return base::StringPrintf(kSystemSettingsDefaultAppsFormat, L"Email"); |
| + |
| + return L"SettingsPageAppsDefaultsProtocolView"; |
| +} |
| + |
| +// Creates the registry key <root hkey>\Software\Classes\<protocol>\URL Protocol |
| +// so that Windows allows the handler for this |protocol| to be be changed. |
| +bool CreateKeyForProtocol(const wchar_t* protocol) { |
| + RegKey reg_key(HKEY_CURRENT_USER, ShellUtil::kRegClasses, KEY_ALL_ACCESS); |
| + DCHECK(reg_key.Valid()); |
| + if (reg_key.CreateKey(protocol, KEY_CREATE_SUB_KEY | KEY_SET_VALUE) != |
| + ERROR_SUCCESS) |
| + return false; |
| + |
| + return reg_key.WriteValue(ShellUtil::kRegUrlProtocol, L"") == ERROR_SUCCESS; |
| +} |
| + |
| } // namespace |
| const wchar_t* ShellUtil::kRegDefaultIcon = L"\\DefaultIcon"; |
| @@ -1815,6 +1844,17 @@ bool ShellUtil::CanMakeChromeDefaultUnattended() { |
| return base::win::GetVersion() < base::win::VERSION_WIN8; |
| } |
| +// static |
| +ShellUtil::SetDefaultInteractiveMode ShellUtil::GetSetDefaultInteractiveMode() { |
| + DCHECK(!CanMakeChromeDefaultUnattended()); |
| + |
| + if (base::win::GetVersion() == base::win::VERSION_WIN8) { |
|
grt (UTC plus 2)
2016/04/15 18:47:26
nit: omit braces
Patrick Monette
2016/04/15 21:35:42
Done.
|
| + return SetDefaultInteractiveMode::INTENT_PICKER; |
| + } |
| + |
| + return SetDefaultInteractiveMode::SYSTEM_SETTINGS; |
| +} |
| + |
| bool ShellUtil::MakeChromeDefault(BrowserDistribution* dist, |
| int shell_change, |
| const base::FilePath& chrome_exe, |
| @@ -1888,7 +1928,7 @@ bool ShellUtil::MakeChromeDefault(BrowserDistribution* dist, |
| bool ShellUtil::ShowMakeChromeDefaultSystemUI( |
| BrowserDistribution* dist, |
| const base::FilePath& chrome_exe) { |
| - DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8); |
| + DCHECK(!CanMakeChromeDefaultUnattended()); |
| if (dist->GetDefaultBrowserControlPolicy() != |
| BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL) { |
| return false; |
| @@ -1900,22 +1940,27 @@ bool ShellUtil::ShowMakeChromeDefaultSystemUI( |
| bool succeeded = true; |
| bool is_default = (GetChromeDefaultState() == IS_DEFAULT); |
| if (!is_default) { |
| - if (base::win::GetVersion() < base::win::VERSION_WIN10) { |
| - // On Windows 8, you can't set yourself as the default handler |
| - // programatically. In other words IApplicationAssociationRegistration |
| - // has been rendered useless. What you can do is to launch |
| - // "Set Program Associations" section of the "Default Programs" |
| - // control panel, which is a mess, or pop the concise "How you want to |
| - // open webpages?" dialog. We choose the latter. |
| - ScopedUserProtocolEntry user_protocol_entry; |
| - succeeded = LaunchSelectDefaultProtocolHandlerDialog(L"http"); |
| - } else { |
| - // On Windows 10, you can't even launch the associations dialog. |
| - // So we launch the settings dialog. Quoting from MSDN: "The Open With |
| - // dialog box can no longer be used to change the default program used to |
| - // open a file extension. You can only use SHOpenWithDialog to open |
| - // a single file." |
| - succeeded = LaunchDefaultAppsSettingsModernDialog(); |
| + switch (GetSetDefaultInteractiveMode()) { |
| + case INTENT_PICKER: { |
| + // On Windows 8, you can't set yourself as the default handler |
| + // programatically. In other words IApplicationAssociationRegistration |
| + // has been rendered useless. What you can do is to launch |
| + // "Set Program Associations" section of the "Default Programs" |
| + // control panel, which is a mess, or pop the concise "How you want to |
| + // open webpages?" dialog. We choose the latter. |
| + ScopedUserProtocolEntry user_protocol_entry; |
| + succeeded = LaunchSelectDefaultProtocolHandlerDialog(L"http"); |
| + } break; |
|
grt (UTC plus 2)
2016/04/15 18:47:26
is this what git cl format likes?
Patrick Monette
2016/04/15 21:35:42
Yes.
|
| + case SYSTEM_SETTINGS: |
| + // On Windows 10, you can't even launch the associations dialog. |
| + // So we launch the settings dialog. Quoting from MSDN: "The Open With |
| + // dialog box can no longer be used to change the default program used |
| + // to open a file extension. You can only use SHOpenWithDialog to open |
| + // a single file." |
| + succeeded = LaunchDefaultAppsSettingsModernDialog( |
| + base::StringPrintf(kSystemSettingsDefaultAppsFormat, L"Browser") |
|
grt (UTC plus 2)
2016/04/15 18:47:26
how about using GetTargetForDefaultAppsSettings(L"
Patrick Monette
2016/04/15 21:35:42
Done.
|
| + .c_str()); |
| + break; |
| } |
| is_default = (succeeded && GetChromeDefaultState() == IS_DEFAULT); |
| } |
| @@ -1977,7 +2022,7 @@ bool ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI( |
| BrowserDistribution* dist, |
| const base::FilePath& chrome_exe, |
| const base::string16& protocol) { |
| - DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8); |
| + DCHECK(!CanMakeChromeDefaultUnattended()); |
| if (dist->GetDefaultBrowserControlPolicy() != |
| BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL) { |
| return false; |
| @@ -1987,17 +2032,36 @@ bool ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI( |
| dist, chrome_exe, base::string16(), protocol, true)) |
| return false; |
| + // This registry key is required so Windows give the user the possibility to |
| + // choose a default handler for |protocol|. |
| + if (!CreateKeyForProtocol(protocol.c_str())) { |
|
grt (UTC plus 2)
2016/04/15 18:47:26
is this doing the same thing that the ScopedUserPr
Patrick Monette
2016/04/15 21:35:42
Good point! I generalized it for all protocols so
|
| + DVLOG(1) << "Failed to create a valid key for the protocol " << protocol; |
| + return false; |
| + } |
| + |
| bool succeeded = true; |
| bool is_default = ( |
| GetChromeDefaultProtocolClientState(protocol) == IS_DEFAULT); |
| if (!is_default) { |
| - // On Windows 8, you can't set yourself as the default handler |
| - // programatically. In other words IApplicationAssociationRegistration |
| - // has been rendered useless. What you can do is to launch |
| - // "Set Program Associations" section of the "Default Programs" |
| - // control panel, which is a mess, or pop the concise "How you want to open |
| - // links of this type (protocol)?" dialog. We choose the latter. |
| - succeeded = LaunchSelectDefaultProtocolHandlerDialog(protocol.c_str()); |
| + switch (GetSetDefaultInteractiveMode()) { |
| + case INTENT_PICKER: { |
| + // On Windows 8, you can't set yourself as the default handler |
| + // programatically. In other words IApplicationAssociationRegistration |
| + // has been rendered useless. What you can do is to launch |
| + // "Set Program Associations" section of the "Default Programs" |
| + // control panel, which is a mess, or pop the concise "How you want to |
| + // open |
| + // links of this type (protocol)?" dialog. We choose the latter. |
| + ScopedUserProtocolEntry user_protocol_entry; |
|
grt (UTC plus 2)
2016/04/15 18:47:26
as-is, this is specifically for http, so it isn't
Patrick Monette
2016/04/15 21:35:41
Done.
|
| + succeeded = LaunchSelectDefaultProtocolHandlerDialog(protocol.c_str()); |
| + } break; |
| + case SYSTEM_SETTINGS: |
| + // On Windows 10, you can't even launch the associations dialog. |
| + // So we launch the settings dialog. |
| + succeeded = LaunchDefaultAppsSettingsModernDialog( |
| + GetTargetForDefaultAppsSettings(protocol).c_str()); |
| + break; |
| + } |
| is_default = (succeeded && |
| GetChromeDefaultProtocolClientState(protocol) == IS_DEFAULT); |
| } |