Fix registerProtocolHandler OS registration on Windows 8
On Windows 8 to register as the default handler for a protocol you need to go through a Windows shell UI flow, it cannot be done anymore via the IApplicationAssociationRegistration interface.
BUG=150850
TEST=Ensure gmail and other protocol handlers can be registered successfully on Windows 7 and 8.
Review URL: https://chromiumcodereview.appspot.com/10963004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@158612 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/custom_handlers/protocol_handler_registry.cc b/chrome/browser/custom_handlers/protocol_handler_registry.cc
index 12ca2f2..5d7cd95 100644
--- a/chrome/browser/custom_handlers/protocol_handler_registry.cc
+++ b/chrome/browser/custom_handlers/protocol_handler_registry.cc
@@ -243,6 +243,11 @@
}
}
+bool ProtocolHandlerRegistry::DefaultClientObserver::
+ IsInteractiveSetDefaultPermitted() {
+ return true;
+}
+
void ProtocolHandlerRegistry::DefaultClientObserver::SetWorker(
ShellIntegration::DefaultProtocolClientWorker* worker) {
worker_ = worker;
diff --git a/chrome/browser/custom_handlers/protocol_handler_registry.h b/chrome/browser/custom_handlers/protocol_handler_registry.h
index 895f510d..25de9550 100644
--- a/chrome/browser/custom_handlers/protocol_handler_registry.h
+++ b/chrome/browser/custom_handlers/protocol_handler_registry.h
@@ -43,6 +43,8 @@
virtual void SetDefaultWebClientUIState(
ShellIntegration::DefaultWebClientUIState state) OVERRIDE;
+ virtual bool IsInteractiveSetDefaultPermitted() OVERRIDE;
+
// Give the observer a handle to the worker, so we can find out the protocol
// when we're called and also tell the worker if we get deleted.
void SetWorker(ShellIntegration::DefaultProtocolClientWorker* worker);
diff --git a/chrome/browser/shell_integration.cc b/chrome/browser/shell_integration.cc
index 08a9684..3c8387e 100644
--- a/chrome/browser/shell_integration.cc
+++ b/chrome/browser/shell_integration.cc
@@ -97,6 +97,12 @@
bool ShellIntegration::SetAsDefaultBrowserInteractive() {
return false;
}
+
+// static
+bool ShellIntegration::SetAsDefaultProtocolClientInteractive(
+ const std::string& protocol) {
+ return false;
+}
#endif
bool ShellIntegration::DefaultWebClientObserver::IsOwnedByWorker() {
@@ -264,5 +270,20 @@
bool ShellIntegration::DefaultProtocolClientWorker::SetAsDefault(
bool interactive_permitted) {
- return ShellIntegration::SetAsDefaultProtocolClient(protocol_);
+ bool result = false;
+ switch (ShellIntegration::CanSetAsDefaultProtocolClient()) {
+ case ShellIntegration::SET_DEFAULT_UNATTENDED:
+ result = ShellIntegration::SetAsDefaultProtocolClient(protocol_);
+ break;
+ case ShellIntegration::SET_DEFAULT_INTERACTIVE:
+ if (interactive_permitted) {
+ result = ShellIntegration::SetAsDefaultProtocolClientInteractive(
+ protocol_);
+ }
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ return result;
}
diff --git a/chrome/browser/shell_integration.h b/chrome/browser/shell_integration.h
index 55ddfcf..ac1ad30 100644
--- a/chrome/browser/shell_integration.h
+++ b/chrome/browser/shell_integration.h
@@ -33,6 +33,14 @@
// (only for the current user). Returns false if this operation fails.
static bool SetAsDefaultProtocolClient(const std::string& protocol);
+ // Initiates an OS shell flow which (if followed by the user) should set
+ // Chrome as the default handler for |protocol|. Returns false if the flow
+ // cannot be initialized, if it is not supported (introduced for Windows 8)
+ // or if the user cancels the operation. This is a blocking call and requires
+ // a FILE thread.
+ static bool SetAsDefaultProtocolClientInteractive(
+ const std::string& protocol);
+
// In Windows 8 a browser can be made default-in-metro only in an interactive
// flow. We will distinguish between two types of permissions here to avoid
// forcing the user into UI interaction when this should not be done.
diff --git a/chrome/browser/shell_integration_win.cc b/chrome/browser/shell_integration_win.cc
index 4782a3c..27b9212 100644
--- a/chrome/browser/shell_integration_win.cc
+++ b/chrome/browser/shell_integration_win.cc
@@ -430,7 +430,7 @@
return false;
}
- string16 wprotocol = UTF8ToUTF16(protocol);
+ string16 wprotocol(UTF8ToUTF16(protocol));
BrowserDistribution* dist = BrowserDistribution::GetDistribution();
if (!ShellUtil::MakeChromeDefaultProtocolClient(dist, chrome_exe.value(),
wprotocol)) {
@@ -456,7 +456,27 @@
return false;
}
- VLOG(1) << "Set-as-default Windows UI triggered.";
+ VLOG(1) << "Set-default-browser Windows UI completed.";
+ return true;
+}
+
+bool ShellIntegration::SetAsDefaultProtocolClientInteractive(
+ const std::string& protocol) {
+ FilePath chrome_exe;
+ if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
+ NOTREACHED() << "Error getting app exe path";
+ return false;
+ }
+
+ BrowserDistribution* dist = BrowserDistribution::GetDistribution();
+ string16 wprotocol(UTF8ToUTF16(protocol));
+ if (!ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(
+ dist, chrome_exe.value(), wprotocol)) {
+ LOG(ERROR) << "Failed to launch the set-default-client Windows UI.";
+ return false;
+ }
+
+ VLOG(1) << "Set-default-client Windows UI completed.";
return true;
}
diff --git a/chrome/installer/util/shell_util.cc b/chrome/installer/util/shell_util.cc
index eb0db53..73aeabe 100644
--- a/chrome/installer/util/shell_util.cc
+++ b/chrome/installer/util/shell_util.cc
@@ -911,6 +911,27 @@
return ret;
}
+// Associates Chrome with |protocol| in the registry. This should not be
+// required on Vista+ but since some applications still read these registry
+// keys directly, we have to do this on Vista+ as well.
+// See http://msdn.microsoft.com/library/aa767914.aspx for more details.
+bool RegisterChromeAsDefaultProtocolClientForXP(BrowserDistribution* dist,
+ const string16& chrome_exe,
+ const string16& protocol) {
+ ScopedVector<RegistryEntry> entries;
+ const string16 chrome_open(ShellUtil::GetChromeShellOpenCmd(chrome_exe));
+ const string16 chrome_icon(ShellUtil::GetChromeIcon(dist, chrome_exe));
+ RegistryEntry::GetUserProtocolEntries(protocol, chrome_icon, chrome_open,
+ &entries);
+ // Change the default protocol handler for current user.
+ if (!AddRegistryEntries(HKEY_CURRENT_USER, entries)) {
+ LOG(ERROR) << "Could not make Chrome default protocol client (XP).";
+ return false;
+ }
+
+ return true;
+}
+
} // namespace
const wchar_t* ShellUtil::kRegDefaultIcon = L"\\DefaultIcon";
@@ -1343,7 +1364,14 @@
if (!dist->CanSetAsDefault())
return false;
- ShellUtil::RegisterChromeForProtocol(dist, chrome_exe, L"", protocol, true);
+ if (!RegisterChromeForProtocol(dist, chrome_exe, string16(), protocol, true))
+ return false;
+
+ // Windows 8 does not permit making a browser default just like that.
+ // This process needs to be routed through the system's UI. Use
+ // ShowMakeChromeDefaultProocolClientSystemUI instead (below).
+ if (!CanMakeChromeDefaultUnattended())
+ return false;
bool ret = true;
// First use the new "recommended" way on Vista to make Chrome default
@@ -1369,18 +1397,34 @@
// Now use the old way to associate Chrome with the desired protocol. This
// should not be required on Vista but since some applications still read
// Software\Classes\http key directly, we have to do this on Vista also.
+ if (!RegisterChromeAsDefaultProtocolClientForXP(dist, chrome_exe, protocol))
+ ret = false;
- ScopedVector<RegistryEntry> entries;
- const string16 suffix(GetCurrentInstallationSuffix(dist, chrome_exe));
- const string16 chrome_open(ShellUtil::GetChromeShellOpenCmd(chrome_exe));
- const string16 chrome_icon(ShellUtil::GetChromeIcon(dist, chrome_exe));
- RegistryEntry::GetUserProtocolEntries(protocol, chrome_icon, chrome_open,
- &entries);
- // Change the default protocol handler for current user.
- if (!AddRegistryEntries(HKEY_CURRENT_USER, entries)) {
- ret = false;
- LOG(ERROR) << "Could not make Chrome default protocol client (XP).";
- }
+ return ret;
+}
+
+bool ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(
+ BrowserDistribution* dist,
+ const string16& chrome_exe,
+ const string16& protocol) {
+ DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8);
+ if (!dist->CanSetAsDefault())
+ return false;
+
+ if (!RegisterChromeForProtocol(dist, chrome_exe, string16(), protocol, true))
+ return false;
+
+ // 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.
+ // Return true only when the user took an action and there was no error.
+ const bool ret = LaunchSelectDefaultProtocolHandlerDialog(protocol.c_str());
+
+ if (ret)
+ RegisterChromeAsDefaultProtocolClientForXP(dist, chrome_exe, protocol);
return ret;
}
diff --git a/chrome/installer/util/shell_util.h b/chrome/installer/util/shell_util.h
index 096b7dc..b6afc95 100644
--- a/chrome/installer/util/shell_util.h
+++ b/chrome/installer/util/shell_util.h
@@ -313,6 +313,18 @@
const string16& chrome_exe,
const string16& protocol);
+ // Shows to the user a system dialog where Chrome can be set as the
+ // default handler for the given protocol. This is intended for Windows 8
+ // and above only. This is a blocking call.
+ //
+ // |dist| gives the type of browser distribution currently in use.
+ // |chrome_exe| The chrome.exe path to register as default browser.
+ // |protocol| is the protocol being registered.
+ static bool ShowMakeChromeDefaultProtocolClientSystemUI(
+ BrowserDistribution* dist,
+ const string16& chrome_exe,
+ const string16& protocol);
+
// Registers Chrome as a potential default browser and handler for filetypes
// and protocols.
// If Chrome is already registered, this method is a no-op.