diff options
author | grt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-17 00:56:06 +0000 |
---|---|---|
committer | grt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-17 00:56:06 +0000 |
commit | ecbff386b900dee166f112ae33593bcead15f746 (patch) | |
tree | 58410969015a53d96569f8f276f595afe7096eee /chrome/browser/shell_integration_win.cc | |
parent | d3cd77b4f574d18ac9146b224f29c846620c2848 (diff) | |
download | chromium_src-ecbff386b900dee166f112ae33593bcead15f746.zip chromium_src-ecbff386b900dee166f112ae33593bcead15f746.tar.gz chromium_src-ecbff386b900dee166f112ae33593bcead15f746.tar.bz2 |
Default browser detection for Windows 8.
Refactored default detection for all versions for better code reuse. IApplicationAssociationRegistration::QueryCurrentDefault is used on Windows 8. For now I'm keeping the Vista/Win7 detection as-is since I don't want to break it. I think the new technique will work there, too, but I don't want to spend time validating this right now.
BUG=113326
TEST=check that chrome can tell whether or not it's the default browser and assorted protocol handler under XP, Vista/Win7, and Win8 at user and system levels.
Review URL: https://chromiumcodereview.appspot.com/10388151
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@137590 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/shell_integration_win.cc')
-rw-r--r-- | chrome/browser/shell_integration_win.cc | 294 |
1 files changed, 160 insertions, 134 deletions
diff --git a/chrome/browser/shell_integration_win.cc b/chrome/browser/shell_integration_win.cc index 14e5f82..869b5b5 100644 --- a/chrome/browser/shell_integration_win.cc +++ b/chrome/browser/shell_integration_win.cc @@ -19,6 +19,7 @@ #include "base/stringprintf.h" #include "base/utf_string_conversions.h" #include "base/win/registry.h" +#include "base/win/scoped_co_mem.h" #include "base/win/scoped_comptr.h" #include "base/win/windows_version.h" #include "chrome/browser/web_applications/web_app.h" @@ -40,6 +41,160 @@ using content::BrowserThread; namespace { +// Gets the short (8.3) form of |path|, putting the result in |short_path| and +// returning true on success. |short_path| is not modified on failure. +bool ShortNameFromPath(const FilePath& path, string16* short_path) { + DCHECK(short_path); + string16 result(MAX_PATH, L'\0'); + DWORD short_length = GetShortPathName(path.value().c_str(), &result[0], + result.size()); + if (short_length == 0 || short_length > result.size()) { + PLOG(ERROR) << "Error getting short (8.3) path"; + return false; + } + + result.resize(short_length); + short_path->swap(result); + return true; +} + +// Probe using IApplicationAssociationRegistration::QueryCurrentDefault +// (Windows 8); see ProbeProtocolHandlers. This mechanism is not suitable for +// use on previous versions of Windows despite the presence of +// QueryCurrentDefault on them since versions of Windows prior to Windows 8 +// did not perform validation on the ProgID registered as the current default. +// As a result, stale ProgIDs could be returned, leading to false positives. +ShellIntegration::DefaultWebClientState ProbeCurrentDefaultHandlers( + const wchar_t* const* protocols, + size_t num_protocols) { + base::win::ScopedComPtr<IApplicationAssociationRegistration> registration; + HRESULT hr = registration.CreateInstance( + CLSID_ApplicationAssociationRegistration, NULL, CLSCTX_INPROC); + if (FAILED(hr)) + return ShellIntegration::UNKNOWN_DEFAULT_WEB_CLIENT; + + string16 prog_id(ShellUtil::kChromeHTMLProgId); + + // If a user specific default browser entry exists, we check for that ProgID + // being default. If not, then the ProgID is ChromeHTML or ChromiumHTML so we + // do not append a suffix to the ProgID. + string16 suffix; + BrowserDistribution* dist = BrowserDistribution::GetDistribution(); + if (ShellUtil::GetUserSpecificDefaultBrowserSuffix(dist, &suffix)) + prog_id += suffix; + + for (size_t i = 0; i < num_protocols; ++i) { + base::win::ScopedCoMem<wchar_t> current_app; + hr = registration->QueryCurrentDefault(protocols[i], AT_URLPROTOCOL, + AL_EFFECTIVE, ¤t_app); + if (FAILED(hr) || prog_id.compare(current_app) != 0) + return ShellIntegration::NOT_DEFAULT_WEB_CLIENT; + } + + return ShellIntegration::IS_DEFAULT_WEB_CLIENT; +} + +// Probe using IApplicationAssociationRegistration::QueryAppIsDefault (Vista and +// Windows 7); see ProbeProtocolHandlers. +ShellIntegration::DefaultWebClientState ProbeAppIsDefaultHandlers( + const wchar_t* const* protocols, + size_t num_protocols) { + base::win::ScopedComPtr<IApplicationAssociationRegistration> registration; + HRESULT hr = registration.CreateInstance( + CLSID_ApplicationAssociationRegistration, NULL, CLSCTX_INPROC); + if (FAILED(hr)) + return ShellIntegration::UNKNOWN_DEFAULT_WEB_CLIENT; + + BrowserDistribution* dist = BrowserDistribution::GetDistribution(); + string16 app_name(dist->GetApplicationName()); + + // If a user specific default browser entry exists, we check for that + // app name being default. If not, then default browser is just called + // Google Chrome or Chromium so we do not append a suffix to the app name. + string16 suffix; + if (ShellUtil::GetUserSpecificDefaultBrowserSuffix(dist, &suffix)) + app_name += suffix; + + BOOL result; + for (size_t i = 0; i < num_protocols; ++i) { + result = TRUE; + hr = registration->QueryAppIsDefault(protocols[i], AT_URLPROTOCOL, + AL_EFFECTIVE, app_name.c_str(), &result); + if (FAILED(hr) || result == FALSE) + return ShellIntegration::NOT_DEFAULT_WEB_CLIENT; + } + + return ShellIntegration::IS_DEFAULT_WEB_CLIENT; +} + +// Probe the current commands registered to handle the shell "open" verb for +// |protocols| (Windows XP); see ProbeProtocolHandlers. +ShellIntegration::DefaultWebClientState ProbeOpenCommandHandlers( + const wchar_t* const* protocols, + size_t num_protocols) { + // Get the path to the current exe (Chrome). + FilePath app_path; + if (!PathService::Get(base::FILE_EXE, &app_path)) { + LOG(ERROR) << "Error getting app exe path"; + return ShellIntegration::UNKNOWN_DEFAULT_WEB_CLIENT; + } + + // Get its short (8.3) form. + string16 short_app_path; + if (!ShortNameFromPath(app_path, &short_app_path)) + return ShellIntegration::UNKNOWN_DEFAULT_WEB_CLIENT; + + const HKEY root_key = HKEY_CLASSES_ROOT; + string16 key_path; + base::win::RegKey key; + string16 value; + CommandLine command_line(CommandLine::NO_PROGRAM); + string16 short_path; + + for (size_t i = 0; i < num_protocols; ++i) { + // Get the command line from HKCU\<protocol>\shell\open\command. + key_path.assign(protocols[i]).append(ShellUtil::kRegShellOpen); + if ((key.Open(root_key, key_path.c_str(), + KEY_QUERY_VALUE) != ERROR_SUCCESS) || + (key.ReadValue(L"", &value) != ERROR_SUCCESS)) { + return ShellIntegration::NOT_DEFAULT_WEB_CLIENT; + } + + // Need to normalize path in case it's been munged. + command_line = CommandLine::FromString(value); + if (!ShortNameFromPath(command_line.GetProgram(), &short_path)) + return ShellIntegration::UNKNOWN_DEFAULT_WEB_CLIENT; + + if (!FilePath::CompareEqualIgnoreCase(short_path, short_app_path)) + return ShellIntegration::NOT_DEFAULT_WEB_CLIENT; + } + + return ShellIntegration::IS_DEFAULT_WEB_CLIENT; +} + +// A helper function that probes default protocol handler registration (in a +// manner appropriate for the current version of Windows) to determine if +// Chrome is the default handler for |protocols|. Returns IS_DEFAULT_WEB_CLIENT +// only if Chrome is the default for all specified protocols. +ShellIntegration::DefaultWebClientState ProbeProtocolHandlers( + const wchar_t* const* protocols, + size_t num_protocols) { + DCHECK(!num_protocols || protocols); + if (DCHECK_IS_ON()) { + for (size_t i = 0; i < num_protocols; ++i) + DCHECK(protocols[i] && *protocols[i]); + } + + const base::win::Version windows_version = base::win::GetVersion(); + + if (windows_version >= base::win::VERSION_WIN8) + return ProbeCurrentDefaultHandlers(protocols, num_protocols); + else if (windows_version >= base::win::VERSION_VISTA) + return ProbeAppIsDefaultHandlers(protocols, num_protocols); + + return ProbeOpenCommandHandlers(protocols, num_protocols); +} + // Helper function for ShellIntegration::GetAppId to generates profile id // from profile path. "profile_id" is composed of sanitized basenames of // user data dir and profile dir joined by a ".". @@ -281,13 +436,6 @@ bool ShellIntegration::SetAsDefaultProtocolClient(const std::string& protocol) { } ShellIntegration::DefaultWebClientState ShellIntegration::IsDefaultBrowser() { - // First determine the app path. If we can't determine what that is, we have - // bigger fish to fry... - FilePath app_path; - if (!PathService::Get(base::FILE_EXE, &app_path)) { - LOG(ERROR) << "Error getting app exe path"; - return UNKNOWN_DEFAULT_WEB_CLIENT; - } // When we check for default browser we don't necessarily want to count file // type handlers and icons as having changed the default browser status, // since the user may have changed their shell settings to cause HTML files @@ -297,67 +445,9 @@ ShellIntegration::DefaultWebClientState ShellIntegration::IsDefaultBrowser() { // re-run the installer or run with the --set-default-browser command line // flag. There is doubtless some other key we can hook into to cause "Repair" // to show up in Add/Remove programs for us. - const string16 kChromeProtocols[] = {L"http", L"https"}; + static const wchar_t* const kChromeProtocols[] = { L"http", L"https" }; - if (base::win::GetVersion() >= base::win::VERSION_VISTA) { - base::win::ScopedComPtr<IApplicationAssociationRegistration> pAAR; - HRESULT hr = pAAR.CreateInstance(CLSID_ApplicationAssociationRegistration, - NULL, CLSCTX_INPROC); - if (!SUCCEEDED(hr)) - return NOT_DEFAULT_WEB_CLIENT; - - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); - string16 app_name = dist->GetApplicationName(); - // If a user specific default browser entry exists, we check for that - // app name being default. If not, then default browser is just called - // Google Chrome or Chromium so we do not append suffix to app name. - string16 suffix; - if (ShellUtil::GetUserSpecificDefaultBrowserSuffix(dist, &suffix)) - app_name += suffix; - - for (int i = 0; i < _countof(kChromeProtocols); i++) { - BOOL result = TRUE; - hr = pAAR->QueryAppIsDefault(kChromeProtocols[i].c_str(), AT_URLPROTOCOL, - AL_EFFECTIVE, app_name.c_str(), &result); - if (!SUCCEEDED(hr) || result == FALSE) { - return NOT_DEFAULT_WEB_CLIENT; - } - } - } else { - string16 short_app_path; - DWORD get_path_result = GetShortPathName(app_path.value().c_str(), - WriteInto(&short_app_path, MAX_PATH), MAX_PATH); - if (!get_path_result || get_path_result > MAX_PATH) { - LOG(ERROR) << "GetShortPathName error in IsDefaultBrowser."; - return UNKNOWN_DEFAULT_WEB_CLIENT; - } - - // open command for protocol associations - for (int i = 0; i < _countof(kChromeProtocols); i++) { - // Check in HKEY_CLASSES_ROOT that is the result of merge between - // HKLM and HKCU - HKEY root_key = HKEY_CLASSES_ROOT; - // Check <protocol>\shell\open\command - string16 key_path(kChromeProtocols[i] + ShellUtil::kRegShellOpen); - base::win::RegKey key(root_key, key_path.c_str(), KEY_READ); - string16 value; - if (!key.Valid() || (key.ReadValue(L"", &value) != ERROR_SUCCESS)) - return NOT_DEFAULT_WEB_CLIENT; - // Need to normalize path in case it's been munged. - CommandLine command_line = CommandLine::FromString(value); - string16 short_path; - get_path_result = GetShortPathName( - command_line.GetProgram().value().c_str(), - WriteInto(&short_path, MAX_PATH), MAX_PATH); - if (!get_path_result || get_path_result > MAX_PATH) { - LOG(ERROR) << "GetShortPathName error in IsDefaultBrowser."; - return UNKNOWN_DEFAULT_WEB_CLIENT; - } - if (!FilePath::CompareEqualIgnoreCase(short_path, short_app_path)) - return NOT_DEFAULT_WEB_CLIENT; - } - } - return IS_DEFAULT_WEB_CLIENT; + return ProbeProtocolHandlers(kChromeProtocols, arraysize(kChromeProtocols)); } ShellIntegration::DefaultWebClientState @@ -365,74 +455,10 @@ ShellIntegration::DefaultWebClientState if (protocol.empty()) return UNKNOWN_DEFAULT_WEB_CLIENT; - // Determine the app path. If we can't determine what that is, we have - // bigger fish to fry... - FilePath app_path; - if (!PathService::Get(base::FILE_EXE, &app_path)) { - LOG(ERROR) << "Error getting app exe path"; - return UNKNOWN_DEFAULT_WEB_CLIENT; - } + string16 wide_protocol(UTF8ToUTF16(protocol)); + const wchar_t* const protocols[] = { wide_protocol.c_str() }; - string16 wprotocol = UTF8ToUTF16(protocol); - - if (base::win::GetVersion() >= base::win::VERSION_WIN8) { - // Windows 8 has removed the ability to ask about the default handlers. - return UNKNOWN_DEFAULT_WEB_CLIENT; - } else if (base::win::GetVersion() >= base::win::VERSION_VISTA) { - base::win::ScopedComPtr<IApplicationAssociationRegistration> pAAR; - HRESULT hr = pAAR.CreateInstance(CLSID_ApplicationAssociationRegistration, - NULL, CLSCTX_INPROC); - if (!SUCCEEDED(hr)) - return NOT_DEFAULT_WEB_CLIENT; - - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); - string16 app_name = dist->GetApplicationName(); - // If a user specific default browser entry exists, we check for that - // app name being default. If not, then default browser is just called - // Google Chrome or Chromium so we do not append suffix to app name. - string16 suffix; - if (ShellUtil::GetUserSpecificDefaultBrowserSuffix(dist, &suffix)) - app_name += suffix; - - BOOL result = TRUE; - hr = pAAR->QueryAppIsDefault(wprotocol.c_str(), AT_URLPROTOCOL, - AL_EFFECTIVE, app_name.c_str(), &result); - if (!SUCCEEDED(hr) || result == FALSE) { - return NOT_DEFAULT_WEB_CLIENT; - } - } else { - string16 short_app_path; - DWORD get_path_result = GetShortPathName(app_path.value().c_str(), - WriteInto(&short_app_path, MAX_PATH), MAX_PATH); - if (!get_path_result || get_path_result > MAX_PATH) { - LOG(ERROR) << "GetShortPathName error in IsDefaultProtocolClient."; - return UNKNOWN_DEFAULT_WEB_CLIENT; - } - - // open command for protocol associations - // Check in HKEY_CLASSES_ROOT that is the result of merge between - // HKLM and HKCU - HKEY root_key = HKEY_CLASSES_ROOT; - // Check <protocol>\shell\open\command - string16 key_path(wprotocol + ShellUtil::kRegShellOpen); - base::win::RegKey key(root_key, key_path.c_str(), KEY_READ); - string16 value; - if (!key.Valid() || (key.ReadValue(L"", &value) != ERROR_SUCCESS)) - return NOT_DEFAULT_WEB_CLIENT; - // Need to normalize path in case it's been munged. - CommandLine command_line = CommandLine::FromString(value); - string16 short_path; - get_path_result = GetShortPathName( - command_line.GetProgram().value().c_str(), - WriteInto(&short_path, MAX_PATH), MAX_PATH); - if (!get_path_result || get_path_result > MAX_PATH) { - LOG(ERROR) << "GetShortPathName error in IsDefaultProtocolClient."; - return UNKNOWN_DEFAULT_WEB_CLIENT; - } - if (!FilePath::CompareEqualIgnoreCase(short_path, short_app_path)) - return NOT_DEFAULT_WEB_CLIENT; - } - return IS_DEFAULT_WEB_CLIENT; + return ProbeProtocolHandlers(protocols, arraysize(protocols)); } // There is no reliable way to say which browser is default on a machine (each |