diff options
author | koz@chromium.org <koz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-26 07:48:02 +0000 |
---|---|---|
committer | koz@chromium.org <koz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-26 07:48:02 +0000 |
commit | 4468a5b324c9e1af0f756ccb4ba5f97a37892eb9 (patch) | |
tree | 467daa8d43f65986353d4f1bb5cf82e71cebd3a8 /chrome/installer/util | |
parent | e31d4c246deaefc59fe4f4fa66a74cd827694a41 (diff) | |
download | chromium_src-4468a5b324c9e1af0f756ccb4ba5f97a37892eb9.zip chromium_src-4468a5b324c9e1af0f756ccb4ba5f97a37892eb9.tar.gz chromium_src-4468a5b324c9e1af0f756ccb4ba5f97a37892eb9.tar.bz2 |
Allow chrome to become the os default handler for arbitrary protocols on mac/win.
Note this is unused currently, but will be hooked up to
navigator.registerProtocolHandler in a subsequent change.
BUG=83556
TEST=Manual
Review URL: http://codereview.chromium.org/6961013
Patch from Ben Wells <benwells@chromium.org>.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@86793 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/installer/util')
-rw-r--r-- | chrome/installer/util/shell_util.cc | 265 | ||||
-rw-r--r-- | chrome/installer/util/shell_util.h | 56 | ||||
-rw-r--r-- | chrome/installer/util/util_constants.cc | 5 | ||||
-rw-r--r-- | chrome/installer/util/util_constants.h | 1 |
4 files changed, 287 insertions, 40 deletions
diff --git a/chrome/installer/util/shell_util.cc b/chrome/installer/util/shell_util.cc index 1d30439..6dad7c7 100644 --- a/chrome/installer/util/shell_util.cc +++ b/chrome/installer/util/shell_util.cc @@ -24,6 +24,7 @@ #include "base/utf_string_conversions.h" #include "base/values.h" #include "base/win/registry.h" +#include "base/win/scoped_comptr.h" #include "base/win/windows_version.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_switches.h" @@ -70,6 +71,27 @@ class RegistryEntry { return true; } + // This method returns a list of the system level registry entries + // needed to declare a capability of handling a protocol. + static bool GetProtocolCapabilityEntries(BrowserDistribution* dist, + const std::wstring& chrome_exe, + const std::wstring& suffix, + const std::wstring& protocol, + std::list<RegistryEntry*>* entries) { + std::wstring app_name = dist->GetApplicationName() + suffix; + std::wstring start_menu_entry(ShellUtil::kRegStartMenuInternet); + start_menu_entry.append(L"\\" + app_name); + + std::wstring capabilities(start_menu_entry + L"\\Capabilities"); + + std::wstring html_prog_id(ShellUtil::kChromeHTMLProgId); + html_prog_id.append(suffix); + entries->push_front(new RegistryEntry(capabilities + L"\\URLAssociations", + protocol, html_prog_id)); + return true; +} + + // This method returns a list of all the system level registry entries that // are needed to register Chromium on the machine. static bool GetSystemEntries(BrowserDistribution* dist, @@ -119,10 +141,11 @@ class RegistryEntry { capabilities + L"\\FileAssociations", ShellUtil::kFileAssociations[i], html_prog_id)); } - for (int i = 0; ShellUtil::kProtocolAssociations[i] != NULL; i++) { + for (int i = 0; ShellUtil::kPotentialProtocolAssociations[i] != NULL; + i++) { entries->push_front(new RegistryEntry( capabilities + L"\\URLAssociations", - ShellUtil::kProtocolAssociations[i], html_prog_id)); + ShellUtil::kPotentialProtocolAssociations[i], html_prog_id)); } FilePath chrome_path(chrome_exe); @@ -137,6 +160,44 @@ class RegistryEntry { } // This method returns a list of all the user level registry entries that + // are needed to make Chromium the default handler for a protocol. + static bool GetUserProtocolEntries(BrowserDistribution* dist, + const std::wstring& chrome_exe, + const std::wstring& suffix, + const std::wstring& protocol, + const std::wstring& chrome_icon, + const std::wstring& chrome_open, + std::list<RegistryEntry*>* entries) { + // Protocols associations. + std::wstring url_key(ShellUtil::kRegClasses); + file_util::AppendToPath(&url_key, protocol); + + // This registry value tells Windows that this 'class' is a URL scheme + // so IE, explorer and other apps will route it to our handler. + // <root hkey>\Software\Classes\<protocol>\URL Protocol + entries->push_front(new RegistryEntry(url_key, + ShellUtil::kRegUrlProtocol, L"")); + + // <root hkey>\Software\Classes\<protocol>\DefaultIcon + std::wstring icon_key = url_key + ShellUtil::kRegDefaultIcon; + entries->push_front(new RegistryEntry(icon_key, chrome_icon)); + + // <root hkey>\Software\Classes\<protocol>\shell\open\command + std::wstring shell_key = url_key + ShellUtil::kRegShellOpen; + entries->push_front(new RegistryEntry(shell_key, chrome_open)); + + // <root hkey>\Software\Classes\<protocol>\shell\open\ddeexec + std::wstring dde_key = url_key + L"\\shell\\open\\ddeexec"; + entries->push_front(new RegistryEntry(dde_key, L"")); + + // <root hkey>\Software\Classes\<protocol>\shell\@ + std::wstring protocol_shell_key = url_key + ShellUtil::kRegShellPath; + entries->push_front(new RegistryEntry(protocol_shell_key, L"open")); + + return true; + } + + // This method returns a list of all the user level registry entries that // are needed to make Chromium default browser. static bool GetUserEntries(BrowserDistribution* dist, const std::wstring& chrome_exe, @@ -154,25 +215,10 @@ class RegistryEntry { // Protocols associations. std::wstring chrome_open = ShellUtil::GetChromeShellOpenCmd(chrome_exe); std::wstring chrome_icon = ShellUtil::GetChromeIcon(dist, chrome_exe); - for (int i = 0; ShellUtil::kProtocolAssociations[i] != NULL; i++) { - std::wstring url_key(ShellUtil::kRegClasses); - file_util::AppendToPath(&url_key, ShellUtil::kProtocolAssociations[i]); - - // <root hkey>\Software\Classes\<protocol>\DefaultIcon - std::wstring icon_key = url_key + ShellUtil::kRegDefaultIcon; - entries->push_front(new RegistryEntry(icon_key, chrome_icon)); - - // <root hkey>\Software\Classes\<protocol>\shell\open\command - std::wstring shell_key = url_key + ShellUtil::kRegShellOpen; - entries->push_front(new RegistryEntry(shell_key, chrome_open)); - - // <root hkey>\Software\Classes\<protocol>\shell\open\ddeexec - std::wstring dde_key = url_key + L"\\shell\\open\\ddeexec"; - entries->push_front(new RegistryEntry(dde_key, L"")); - - // <root hkey>\Software\Classes\<protocol>\shell\@ - std::wstring protocol_shell_key = url_key + ShellUtil::kRegShellPath; - entries->push_front(new RegistryEntry(protocol_shell_key, L"open")); + for (int i = 0; ShellUtil::kBrowserProtocolAssociations[i] != NULL; i++) { + GetUserProtocolEntries(dist, chrome_exe, suffix, + ShellUtil::kBrowserProtocolAssociations[i], + chrome_icon, chrome_open, entries); } // start->Internet shortcut. @@ -297,13 +343,36 @@ bool IsChromeRegistered(BrowserDistribution* dist, return registered; } +// This method checks if Chrome is already registered on the local machine +// for the requested protocol. It just checks the one value required for this. +bool IsChromeRegisteredForProtocol(BrowserDistribution* dist, + const std::wstring& chrome_exe, + const std::wstring& suffix, + const std::wstring& protocol) { + bool registered = true; + std::list<RegistryEntry*> entries; + STLElementDeleter<std::list<RegistryEntry*>> entries_deleter(&entries); + RegistryEntry::GetProtocolCapabilityEntries(dist, chrome_exe, suffix, + protocol, &entries); + for (std::list<RegistryEntry*>::const_iterator itr = entries.begin(); + itr != entries.end() && registered; ++itr) { + // We do not need registered = registered && ... since the loop condition + // is set to exit early. + registered = (*itr)->ExistsInHKLM(); + } + return registered; +} + // This method registers Chrome on Vista by launching an elevated setup.exe. // That will show the user the standard Vista elevation prompt. If the user // accepts it the new process will make the necessary changes and return SUCCESS // that we capture and return. +// If protocol is non-empty we will also register Chrome as being capable of +// handling the protocol. bool ElevateAndRegisterChrome(BrowserDistribution* dist, const std::wstring& chrome_exe, - const std::wstring& suffix) { + const std::wstring& suffix, + const std::wstring& protocol) { FilePath exe_path = FilePath::FromWStringHack(chrome_exe).DirName() .Append(installer::kSetupExe); @@ -331,6 +400,11 @@ bool ElevateAndRegisterChrome(BrowserDistribution* dist, cmd.AppendSwitch(installer::switches::kChromeFrame); } + if (!protocol.empty()) { + cmd.AppendSwitchNative( + installer::switches::kRegisterURLProtocol, protocol); + } + DWORD ret_val = 0; InstallUtil::ExecuteExeAsAdmin(cmd, &ret_val); if (ret_val == 0) @@ -396,7 +470,6 @@ bool AnotherUserHasDefaultBrowser(BrowserDistribution* dist, } // namespace - const wchar_t* ShellUtil::kRegDefaultIcon = L"\\DefaultIcon"; const wchar_t* ShellUtil::kRegShellPath = L"\\shell"; const wchar_t* ShellUtil::kRegShellOpen = L"\\shell\\open\\command"; @@ -422,8 +495,10 @@ const wchar_t* ShellUtil::kChromeHTMLProgIdDesc = L"Chromium HTML Document"; const wchar_t* ShellUtil::kFileAssociations[] = {L".htm", L".html", L".shtml", L".xht", L".xhtml", NULL}; -const wchar_t* ShellUtil::kProtocolAssociations[] = {L"ftp", L"http", L"https", - NULL}; +const wchar_t* ShellUtil::kBrowserProtocolAssociations[] = {L"ftp", L"http", + L"https", NULL}; +const wchar_t* ShellUtil::kPotentialProtocolAssociations[] = {L"ftp", L"http", + L"https", L"mailto", L"webcal", NULL}; const wchar_t* ShellUtil::kRegUrlProtocol = L"URL Protocol"; bool ShellUtil::AdminNeededForRegistryCleanup(BrowserDistribution* dist, @@ -636,22 +711,32 @@ bool ShellUtil::MakeChromeDefault(BrowserDistribution* dist, // browser. if (base::win::GetVersion() >= base::win::VERSION_VISTA) { VLOG(1) << "Registering Chrome as default browser on Vista."; - IApplicationAssociationRegistration* pAAR; - HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration, - NULL, CLSCTX_INPROC, __uuidof(IApplicationAssociationRegistration), - (void**)&pAAR); + base::win::ScopedComPtr<IApplicationAssociationRegistration> pAAR; + HRESULT hr = pAAR.CreateInstance(CLSID_ApplicationAssociationRegistration, + NULL, CLSCTX_INPROC); if (SUCCEEDED(hr)) { std::wstring app_name = dist->GetApplicationName(); std::wstring suffix; if (ShellUtil::GetUserSpecificDefaultBrowserSuffix(dist, &suffix)) app_name += suffix; - hr = pAAR->SetAppAsDefaultAll(app_name.c_str()); - pAAR->Release(); + for (int i = 0; + SUCCEEDED(hr) && ShellUtil::kBrowserProtocolAssociations[i] != NULL; + i++) { + hr = pAAR->SetAppAsDefault(app_name.c_str(), + ShellUtil::kBrowserProtocolAssociations[i], AT_URLPROTOCOL); + } + + for (int i = 0; + SUCCEEDED(hr) && ShellUtil::kFileAssociations[i] != NULL; i++) { + hr = pAAR->SetAppAsDefault(app_name.c_str(), + ShellUtil::kFileAssociations[i], AT_FILEEXTENSION); + } } if (!SUCCEEDED(hr)) { ret = false; - LOG(ERROR) << "Could not make Chrome default browser."; + LOG(ERROR) << "Could not make Chrome default browser (Vista): HRESULT=" + << hr << "."; } } @@ -668,13 +753,17 @@ bool ShellUtil::MakeChromeDefault(BrowserDistribution* dist, RegistryEntry::GetUserEntries(dist, chrome_exe, suffix, &entries); // Change the default browser for current user. if ((shell_change & ShellUtil::CURRENT_USER) && - !AddRegistryEntries(HKEY_CURRENT_USER, entries)) - ret = false; + !AddRegistryEntries(HKEY_CURRENT_USER, entries)) { + ret = false; + LOG(ERROR) << "Could not make Chrome default browser (XP/current user)."; + } // Chrome as default browser at system level. if ((shell_change & ShellUtil::SYSTEM_LEVEL) && - !AddRegistryEntries(HKEY_LOCAL_MACHINE, entries)) + !AddRegistryEntries(HKEY_LOCAL_MACHINE, entries)) { ret = false; + LOG(ERROR) << "Could not make Chrome default browser (XP/system level)."; + } // Send Windows notification event so that it can update icons for // file associations. @@ -682,6 +771,61 @@ bool ShellUtil::MakeChromeDefault(BrowserDistribution* dist, return ret; } +bool ShellUtil::MakeChromeDefaultProtocolClient(BrowserDistribution* dist, + const std::wstring& chrome_exe, + const std::wstring& protocol) { + if (!dist->CanSetAsDefault()) + return false; + + ShellUtil::RegisterChromeForProtocol(dist, chrome_exe, L"", protocol, true); + + bool ret = true; + // First use the new "recommended" way on Vista to make Chrome default + // protocol handler. + if (base::win::GetVersion() >= base::win::VERSION_VISTA) { + VLOG(1) << "Registering Chrome as default handler for " << protocol + << " on Vista."; + base::win::ScopedComPtr<IApplicationAssociationRegistration> pAAR; + HRESULT hr = pAAR.CreateInstance(CLSID_ApplicationAssociationRegistration, + NULL, CLSCTX_INPROC); + if (SUCCEEDED(hr)) { + std::wstring app_name = dist->GetApplicationName(); + std::wstring suffix; + if (ShellUtil::GetUserSpecificDefaultBrowserSuffix(dist, &suffix)) + app_name += suffix; + + hr = pAAR->SetAppAsDefault(app_name.c_str(), protocol.c_str(), + AT_URLPROTOCOL); + } + if (!SUCCEEDED(hr)) { + ret = false; + LOG(ERROR) << "Could not make Chrome default protocol client (Vista):" + << " HRESULT=" << hr << "."; + } + } + + // 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. + + std::list<RegistryEntry*> entries; + STLElementDeleter<std::list<RegistryEntry*>> entries_deleter(&entries); + std::wstring suffix; + if (!GetUserSpecificDefaultBrowserSuffix(dist, &suffix)) + suffix = L""; + std::wstring chrome_open = ShellUtil::GetChromeShellOpenCmd(chrome_exe); + std::wstring chrome_icon = ShellUtil::GetChromeIcon(dist, chrome_exe); + RegistryEntry::GetUserProtocolEntries(dist, chrome_exe, suffix, 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::RegisterChromeBrowser(BrowserDistribution* dist, const std::wstring& chrome_exe, const std::wstring& unique_suffix, @@ -716,7 +860,7 @@ bool ShellUtil::RegisterChromeBrowser(BrowserDistribution* dist, // If user is not an admin and OS is Vista, try to elevate and register. if (elevate_if_not_admin && base::win::GetVersion() >= base::win::VERSION_VISTA && - ElevateAndRegisterChrome(dist, chrome_exe, suffix)) + ElevateAndRegisterChrome(dist, chrome_exe, suffix, L"")) return true; // If we got to this point then all we can do is create ProgIds under HKCU @@ -727,6 +871,55 @@ bool ShellUtil::RegisterChromeBrowser(BrowserDistribution* dist, return AddRegistryEntries(HKEY_CURRENT_USER, entries); } +bool ShellUtil::RegisterChromeForProtocol(BrowserDistribution* dist, + const std::wstring& chrome_exe, + const std::wstring& unique_suffix, + const std::wstring& protocol, + bool elevate_if_not_admin) { + if (!dist->CanSetAsDefault()) + return false; + + // Figure out we need to append a suffix to the registry entries to + // make them unique. + std::wstring suffix; + if (!unique_suffix.empty()) { + suffix = unique_suffix; + } else if (InstallUtil::IsPerUserInstall(chrome_exe.c_str()) && + !GetUserSpecificDefaultBrowserSuffix(dist, &suffix) && + !AnotherUserHasDefaultBrowser(dist, chrome_exe)) { + suffix = L""; + } + + // Check if Chromium is already registered with this suffix. + if (IsChromeRegisteredForProtocol(dist, chrome_exe, suffix, protocol)) + return true; + + if (IsUserAnAdmin()) { + // We can do this operation directly. + // If we're not registered at all, try to register. If that fails + // we should give up. + if (!IsChromeRegistered(dist, chrome_exe, suffix) && + !RegisterChromeBrowser(dist, chrome_exe, suffix, false)) { + return false; + } + + // Write in the capabillity for the protocol. + std::list<RegistryEntry*> entries; + STLElementDeleter<std::list<RegistryEntry*>> entries_deleter(&entries); + RegistryEntry::GetProtocolCapabilityEntries(dist, chrome_exe, suffix, + protocol, &entries); + return AddRegistryEntries(HKEY_LOCAL_MACHINE, entries); + } else if (elevate_if_not_admin && + base::win::GetVersion() >= base::win::VERSION_VISTA) { + // Elevate to do the whole job + return ElevateAndRegisterChrome(dist, chrome_exe, suffix, protocol); + } else { + // we need admin rights to register our capability. If we don't + // have them and can't elevate, give up. + return false; + } +} + bool ShellUtil::RemoveChromeDesktopShortcut(BrowserDistribution* dist, int shell_change, bool alternate) { std::wstring shortcut_name; diff --git a/chrome/installer/util/shell_util.h b/chrome/installer/util/shell_util.h index b34f4f6..fcb0fb8 100644 --- a/chrome/installer/util/shell_util.h +++ b/chrome/installer/util/shell_util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // @@ -30,6 +30,9 @@ class ShellUtil { SYSTEM_LEVEL = 0x2 // Make any shell changes only at the system level }; + // Relative path of the URL Protocol registry entry (prefixed with '\'). + static const wchar_t* kRegURLProtocol; + // Relative path of DefaultIcon registry entry (prefixed with '\'). static const wchar_t* kRegDefaultIcon; @@ -69,8 +72,12 @@ class ShellUtil { // File extensions that Chrome registers itself for. static const wchar_t* kFileAssociations[]; - // Protocols that Chrome registers itself for. - static const wchar_t* kProtocolAssociations[]; + // Protocols that Chrome registers itself as the default handler for + // when the user makes Chrome the default browser. + static const wchar_t* kBrowserProtocolAssociations[]; + + // Protocols that Chrome registers itself as being capable of handling. + static const wchar_t* kPotentialProtocolAssociations[]; // Registry value name that is needed for ChromeHTML ProgId static const wchar_t* kRegUrlProtocol; @@ -156,7 +163,18 @@ class ShellUtil { static bool GetUserSpecificDefaultBrowserSuffix(BrowserDistribution* dist, std::wstring* entry); - // Make Chrome default browser. + // Make Chrome the default browser. This function works by going through + // the url protocols and file associations that are related to general + // browsing, e.g. http, https, .html etc., and requesting to become the + // default handler for each. If any of these fails the operation will return + // false to indicate failure, which is consistent with the return value of + // ShellIntegration::IsDefaultBrowser. + // + // In the case of failure any successful changes will be left, however no + // more changes will be attempted. + // TODO(benwells): Attempt to undo any changes that were successfully made. + // http://crbug.com/83970 + // // shell_change: Defined whether to register as default browser at system // level or user level. If value has ShellChange::SYSTEM_LEVEL // we should be running as admin user. @@ -168,6 +186,13 @@ class ShellUtil { const std::wstring& chrome_exe, bool elevate_if_not_admin); + // Make Chrome the default application for a protocol. + // chrome_exe: The chrome.exe path to register as default browser. + // protocol: The protocol to register as the default handler for. + static bool MakeChromeDefaultProtocolClient(BrowserDistribution* dist, + const std::wstring& chrome_exe, + const std::wstring& protocol); + // This method adds Chrome to the list that shows up in Add/Remove Programs-> // Set Program Access and Defaults and also creates Chrome ProgIds under // Software\Classes. This method requires write access to HKLM so is just @@ -194,6 +219,29 @@ class ShellUtil { const std::wstring& unique_suffix, bool elevate_if_not_admin); + // This method declares to Windows that Chrome is capable of handling the + // given protocol. This function will call the RegisterChromeBrowser function + // to register with Windows as capable of handling the protocol, if it isn't + // currently registered as capable. + // Declaring the capability of handling a protocol is necessary to register + // as the default handler for the protocol in Vista and later versions of + // Windows. + // + // If called by the browser and elevation is required, it will elevate by + // calling setup.exe which will again call this function with elevate false. + // + // |chrome_exe| full path to chrome.exe. + // |unique_suffix| Optional input. If given, this function appends the value + // to default browser entries names that it creates in the registry. + // |protocol| The protocol to register as being capable of handling.s + // |elevate_if_not_admin| if true will make this method try alternate methods + // as described above. + static bool RegisterChromeForProtocol(BrowserDistribution* dist, + const std::wstring& chrome_exe, + const std::wstring& unique_suffix, + const std::wstring& protocol, + bool elevate_if_not_admin); + // Remove Chrome shortcut from Desktop. // If shell_change is CURRENT_USER, the shortcut is removed from the // Desktop folder of current user's profile. diff --git a/chrome/installer/util/util_constants.cc b/chrome/installer/util/util_constants.cc index bd92223..bfc1007 100644 --- a/chrome/installer/util/util_constants.cc +++ b/chrome/installer/util/util_constants.cc @@ -106,6 +106,11 @@ const char kRegisterChromeBrowser[] = "register-chrome-browser"; const char kRegisterChromeBrowserSuffix[] = "register-chrome-browser-suffix"; +// Switch to allow an extra URL protocol to be registered. This option is used +// in conjunction with kRegisterChromeBrowser to specify an extra protocol +// in addition to the standard set of protocols. +const char kRegisterURLProtocol[] = "register-url-protocol"; + // Renames chrome.exe to old_chrome.exe and renames new_chrome.exe to chrome.exe // to support in-use updates. Also deletes opv key. const char kRenameChromeExe[] = "rename-chrome-exe"; diff --git a/chrome/installer/util/util_constants.h b/chrome/installer/util/util_constants.h index 9153b7b..16cb791 100644 --- a/chrome/installer/util/util_constants.h +++ b/chrome/installer/util/util_constants.h @@ -136,6 +136,7 @@ extern const char kMultiInstall[]; extern const char kNewSetupExe[]; extern const char kRegisterChromeBrowser[]; extern const char kRegisterChromeBrowserSuffix[]; +extern const char kRegisterURLProtocol[]; extern const char kRenameChromeExe[]; extern const char kRemoveChromeRegistration[]; extern const char kRunAsAdmin[]; |