| // Copyright (c) 2010 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. |
| // |
| // This file defines functions that integrate Chrome in Windows shell. These |
| // functions can be used by Chrome as well as Chrome installer. All of the |
| // work is done by the local functions defined in anonymous namespace in |
| // this class. |
| |
| #include "chrome/installer/util/shell_util.h" |
| |
| #include <windows.h> |
| #include <shlobj.h> |
| |
| #include "base/command_line.h" |
| #include "base/file_path.h" |
| #include "base/file_util.h" |
| #include "base/logging.h" |
| #include "base/scoped_ptr.h" |
| #include "base/stl_util-inl.h" |
| #include "base/string_number_conversions.h" |
| #include "base/string_split.h" |
| #include "base/string_util.h" |
| #include "base/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "base/win/registry.h" |
| #include "base/win/windows_version.h" |
| #include "chrome/common/chrome_constants.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/installer/util/browser_distribution.h" |
| #include "chrome/installer/util/install_util.h" |
| #include "chrome/installer/util/master_preferences.h" |
| |
| #include "installer_util_strings.h" |
| |
| using base::win::RegKey; |
| |
| namespace { |
| |
| // This class represents a single registry entry. The objective is to |
| // encapsulate all the registry entries required for registering Chrome at one |
| // place. This class can not be instantiated outside the class and the objects |
| // of this class type can be obtained only by calling a static method of this |
| // class. |
| class RegistryEntry { |
| public: |
| // This method returns a list of all the registry entries that |
| // are needed to register Chromium ProgIds. |
| static bool GetProgIdEntries(const std::wstring& chrome_exe, |
| const std::wstring& suffix, |
| std::list<RegistryEntry*>* entries) { |
| std::wstring icon_path = ShellUtil::GetChromeIcon(chrome_exe); |
| std::wstring open_cmd = ShellUtil::GetChromeShellOpenCmd(chrome_exe); |
| |
| // File association ProgId |
| std::wstring chrome_html_prog_id(ShellUtil::kRegClasses); |
| file_util::AppendToPath(&chrome_html_prog_id, ShellUtil::kChromeHTMLProgId); |
| chrome_html_prog_id.append(suffix); |
| entries->push_front(new RegistryEntry( |
| chrome_html_prog_id, ShellUtil::kChromeHTMLProgIdDesc)); |
| entries->push_front(new RegistryEntry( |
| chrome_html_prog_id, ShellUtil::kRegUrlProtocol, L"")); |
| entries->push_front(new RegistryEntry( |
| chrome_html_prog_id + ShellUtil::kRegDefaultIcon, icon_path)); |
| entries->push_front(new RegistryEntry( |
| chrome_html_prog_id + ShellUtil::kRegShellOpen, open_cmd)); |
| |
| 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(const std::wstring& chrome_exe, |
| const std::wstring& suffix, |
| std::list<RegistryEntry*>* entries) { |
| std::wstring icon_path = ShellUtil::GetChromeIcon(chrome_exe); |
| std::wstring quoted_exe_path = L"\"" + chrome_exe + L"\""; |
| |
| BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
| std::wstring app_name = dist->GetApplicationName() + suffix; |
| std::wstring start_menu_entry(ShellUtil::kRegStartMenuInternet); |
| start_menu_entry.append(L"\\" + app_name); |
| entries->push_front(new RegistryEntry(start_menu_entry, app_name)); |
| entries->push_front(new RegistryEntry( |
| start_menu_entry + ShellUtil::kRegShellOpen, quoted_exe_path)); |
| entries->push_front(new RegistryEntry( |
| start_menu_entry + ShellUtil::kRegDefaultIcon, icon_path)); |
| |
| std::wstring install_info(start_menu_entry + L"\\InstallInfo"); |
| // TODO: use CommandLine API instead of constructing command lines. |
| entries->push_front(new RegistryEntry(install_info, L"ReinstallCommand", |
| quoted_exe_path + L" --" + ASCIIToWide(switches::kMakeDefaultBrowser))); |
| entries->push_front(new RegistryEntry(install_info, L"HideIconsCommand", |
| quoted_exe_path + L" --" + ASCIIToWide(switches::kHideIcons))); |
| entries->push_front(new RegistryEntry(install_info, L"ShowIconsCommand", |
| quoted_exe_path + L" --" + ASCIIToWide(switches::kShowIcons))); |
| entries->push_front(new RegistryEntry(install_info, L"IconsVisible", 1)); |
| |
| std::wstring capabilities(start_menu_entry + L"\\Capabilities"); |
| entries->push_front(new RegistryEntry(ShellUtil::kRegRegisteredApplications, |
| app_name, capabilities)); |
| entries->push_front(new RegistryEntry( |
| capabilities, L"ApplicationDescription", |
| dist->GetLongAppDescription())); |
| entries->push_front(new RegistryEntry( |
| capabilities, L"ApplicationIcon", icon_path)); |
| entries->push_front(new RegistryEntry( |
| capabilities, L"ApplicationName", app_name)); |
| |
| entries->push_front(new RegistryEntry(capabilities + L"\\StartMenu", |
| L"StartMenuInternet", app_name)); |
| |
| std::wstring html_prog_id(ShellUtil::kChromeHTMLProgId); |
| html_prog_id.append(suffix); |
| for (int i = 0; ShellUtil::kFileAssociations[i] != NULL; i++) { |
| entries->push_front(new RegistryEntry( |
| capabilities + L"\\FileAssociations", |
| ShellUtil::kFileAssociations[i], html_prog_id)); |
| } |
| for (int i = 0; ShellUtil::kProtocolAssociations[i] != NULL; i++) { |
| entries->push_front(new RegistryEntry( |
| capabilities + L"\\URLAssociations", |
| ShellUtil::kProtocolAssociations[i], html_prog_id)); |
| } |
| |
| FilePath chrome_path(chrome_exe); |
| std::wstring app_path_key(ShellUtil::kAppPathsRegistryKey); |
| file_util::AppendToPath(&app_path_key, chrome_path.BaseName().value()); |
| entries->push_front(new RegistryEntry(app_path_key, chrome_exe)); |
| entries->push_front(new RegistryEntry(app_path_key, |
| ShellUtil::kAppPathsRegistryPathName, chrome_path.DirName().value())); |
| |
| // TODO: add chrome to open with list (Bug 16726). |
| 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(const std::wstring& chrome_exe, |
| const std::wstring& suffix, |
| std::list<RegistryEntry*>* entries) { |
| // File extension associations. |
| std::wstring html_prog_id(ShellUtil::kChromeHTMLProgId); |
| html_prog_id.append(suffix); |
| for (int i = 0; ShellUtil::kFileAssociations[i] != NULL; i++) { |
| std::wstring ext_key(ShellUtil::kRegClasses); |
| file_util::AppendToPath(&ext_key, ShellUtil::kFileAssociations[i]); |
| entries->push_front(new RegistryEntry(ext_key, html_prog_id)); |
| } |
| |
| // Protocols associations. |
| std::wstring chrome_open = ShellUtil::GetChromeShellOpenCmd(chrome_exe); |
| std::wstring chrome_icon = ShellUtil::GetChromeIcon(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")); |
| } |
| |
| // start->Internet shortcut. |
| std::wstring start_menu(ShellUtil::kRegStartMenuInternet); |
| BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
| std::wstring app_name = dist->GetApplicationName() + suffix; |
| entries->push_front(new RegistryEntry(start_menu, app_name)); |
| return true; |
| } |
| |
| // Generate work_item tasks required to create current registry entry and |
| // add them to the given work item list. |
| void AddToWorkItemList(HKEY root, WorkItemList *items) const { |
| items->AddCreateRegKeyWorkItem(root, _key_path); |
| if (_is_string) { |
| items->AddSetRegValueWorkItem(root, _key_path, _name, _value, true); |
| } else { |
| items->AddSetRegValueWorkItem(root, _key_path, _name, _int_value, true); |
| } |
| } |
| |
| // Checks if the current registry entry exists in HKLM registry and the value |
| // is same. |
| bool ExistsInHKLM() const { |
| RegKey key(HKEY_LOCAL_MACHINE, _key_path.c_str(), KEY_READ); |
| bool found = false; |
| if (_is_string) { |
| std::wstring read_value; |
| found = (key.ReadValue(_name.c_str(), &read_value)) && |
| (read_value.size() == _value.size()) && |
| (std::equal(_value.begin(), _value.end(), read_value.begin(), |
| CaseInsensitiveCompare<wchar_t>())); |
| } else { |
| DWORD read_value; |
| found = key.ReadValueDW(_name.c_str(), &read_value) && |
| read_value == _int_value; |
| } |
| key.Close(); |
| return found; |
| } |
| |
| // Checks if the current registry entry exists in HKLM registry |
| // (only the name). |
| bool NameExistsInHKLM() const { |
| RegKey key(HKEY_LOCAL_MACHINE, _key_path.c_str(), KEY_READ); |
| bool found = false; |
| if (_is_string) { |
| std::wstring read_value; |
| found = key.ReadValue(_name.c_str(), &read_value); |
| } else { |
| DWORD read_value; |
| found = key.ReadValueDW(_name.c_str(), &read_value); |
| } |
| key.Close(); |
| return found; |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(RegistryEntry); |
| |
| // Create a object that represent default value of a key |
| RegistryEntry(const std::wstring& key_path, const std::wstring& value) |
| : _key_path(key_path), _name(), |
| _is_string(true), _value(value), _int_value(0) { |
| } |
| |
| // Create a object that represent a key of type REG_SZ |
| RegistryEntry(const std::wstring& key_path, const std::wstring& name, |
| const std::wstring& value) |
| : _key_path(key_path), _name(name), |
| _is_string(true), _value(value), _int_value(0) { |
| } |
| |
| // Create a object that represent a key of integer type |
| RegistryEntry(const std::wstring& key_path, const std::wstring& name, |
| DWORD value) |
| : _key_path(key_path), _name(name), |
| _is_string(false), _value(), _int_value(value) { |
| } |
| |
| std::wstring _key_path; // key path for the registry entry |
| std::wstring _name; // name of the registry entry |
| bool _is_string; // true if current registry entry is of type REG_SZ |
| std::wstring _value; // string value (useful if _is_string = true) |
| DWORD _int_value; // integer value (useful if _is_string = false) |
| }; // class RegistryEntry |
| |
| |
| // This method converts all the RegistryEntries from the given list to |
| // Set/CreateRegWorkItems and runs them using WorkItemList. |
| bool AddRegistryEntries(HKEY root, const std::list<RegistryEntry*>& entries) { |
| scoped_ptr<WorkItemList> items(WorkItem::CreateWorkItemList()); |
| |
| for (std::list<RegistryEntry*>::const_iterator itr = entries.begin(); |
| itr != entries.end(); ++itr) |
| (*itr)->AddToWorkItemList(root, items.get()); |
| |
| // Apply all the registry changes and if there is a problem, rollback |
| if (!items->Do()) { |
| items->Rollback(); |
| return false; |
| } |
| return true; |
| } |
| |
| // This method checks if Chrome is already registered on the local machine. |
| // It gets all the required registry entries for Chrome and then checks if |
| // they exist in HKLM. Returns true if all the entries exist, otherwise false. |
| bool IsChromeRegistered(const std::wstring& chrome_exe, |
| const std::wstring& suffix) { |
| bool registered = true; |
| std::list<RegistryEntry*> entries; |
| STLElementDeleter<std::list<RegistryEntry*>> entries_deleter(&entries); |
| RegistryEntry::GetProgIdEntries(chrome_exe, suffix, &entries); |
| RegistryEntry::GetSystemEntries(chrome_exe, suffix, &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 eleavated setup.exe. |
| // That will show user standard Vista elevation prompt. If user accepts it |
| // the new process will make the necessary changes and return SUCCESS that |
| // we capture and return. |
| bool ElevateAndRegisterChrome(const std::wstring& chrome_exe, |
| const std::wstring& suffix) { |
| FilePath exe_path = |
| FilePath::FromWStringHack(chrome_exe).DirName() |
| .Append(installer_util::kSetupExe); |
| if (!file_util::PathExists(exe_path)) { |
| BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
| HKEY reg_root = InstallUtil::IsPerUserInstall(chrome_exe.c_str()) ? |
| HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; |
| RegKey key(reg_root, dist->GetUninstallRegPath().c_str(), KEY_READ); |
| std::wstring uninstall_string; |
| key.ReadValue(installer_util::kUninstallStringField, &uninstall_string); |
| CommandLine command_line = CommandLine::FromString(uninstall_string); |
| exe_path = command_line.GetProgram(); |
| } |
| if (file_util::PathExists(exe_path)) { |
| std::wstring params(L"--"); |
| params.append( |
| ASCIIToWide(installer_util::switches::kRegisterChromeBrowser)); |
| params.append(L"=\"" + chrome_exe + L"\""); |
| if (!suffix.empty()) { |
| params.append(L" --"); |
| params.append(ASCIIToWide( |
| installer_util::switches::kRegisterChromeBrowserSuffix)); |
| params.append(L"=\"" + suffix + L"\""); |
| } |
| |
| CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); |
| if (browser_command_line.HasSwitch(switches::kChromeFrame)) { |
| params.append(L" --"); |
| params.append(installer_util::switches::kChromeFrame); |
| } |
| |
| DWORD ret_val = 0; |
| InstallUtil::ExecuteExeAsAdmin(exe_path.value(), params, &ret_val); |
| if (ret_val == 0) |
| return true; |
| } |
| return false; |
| } |
| |
| // This method tries to figure out if another user has already registered her |
| // own copy of Chrome so that we can avoid overwriting it and append current |
| // user's login name to default browser registry entries. This function is |
| // not meant to detect all cases. It just tries to handle the most common case. |
| // All the conditions below have to be true for it to return true: |
| // - Software\Clients\StartMenuInternet\Chromium\"" key should have a valid |
| // value. |
| // - The value should not be same as given value in |chrome_exe| |
| // - Finally to handle the default install path (C:\Document and Settings\ |
| // <user>\Local Settings\Application Data\Chromium\Application) the value |
| // of the above key should differ from |chrome_exe| only in user name. |
| bool AnotherUserHasDefaultBrowser(const std::wstring& chrome_exe) { |
| std::wstring reg_key(ShellUtil::kRegStartMenuInternet); |
| BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
| reg_key.append(L"\\" + dist->GetApplicationName() + ShellUtil::kRegShellOpen); |
| RegKey key(HKEY_LOCAL_MACHINE, reg_key.c_str(), KEY_READ); |
| std::wstring registry_chrome_exe; |
| if (!key.ReadValue(L"", ®istry_chrome_exe) || |
| registry_chrome_exe.length() < 2) |
| return false; |
| |
| registry_chrome_exe = registry_chrome_exe.substr(1, |
| registry_chrome_exe.length() - 2); |
| if ((registry_chrome_exe.size() == chrome_exe.size()) && |
| (std::equal(chrome_exe.begin(), chrome_exe.end(), |
| registry_chrome_exe.begin(), |
| CaseInsensitiveCompare<wchar_t>()))) |
| return false; |
| |
| std::vector<std::wstring> v1, v2; |
| base::SplitString(registry_chrome_exe, L'\\', &v1); |
| base::SplitString(chrome_exe, L'\\', &v2); |
| if (v1.size() == 0 || v2.size() == 0 || v1.size() != v2.size()) |
| return false; |
| |
| // Now check that only one of the values within two '\' chars differ. |
| std::vector<std::wstring>::iterator itr1 = v1.begin(); |
| std::vector<std::wstring>::iterator itr2 = v2.begin(); |
| bool one_mismatch = false; |
| for ( ; itr1 < v1.end() && itr2 < v2.end(); ++itr1, ++itr2) { |
| std::wstring s1 = *itr1; |
| std::wstring s2 = *itr2; |
| if ((s1.size() != s2.size()) || |
| (!std::equal(s1.begin(), s1.end(), |
| s2.begin(), CaseInsensitiveCompare<wchar_t>()))) { |
| if (one_mismatch) |
| return false; |
| else |
| one_mismatch = true; |
| } |
| } |
| return true; |
| } |
| |
| } // namespace |
| |
| |
| const wchar_t* ShellUtil::kRegDefaultIcon = L"\\DefaultIcon"; |
| const wchar_t* ShellUtil::kRegShellPath = L"\\shell"; |
| const wchar_t* ShellUtil::kRegShellOpen = L"\\shell\\open\\command"; |
| const wchar_t* ShellUtil::kRegStartMenuInternet = |
| L"Software\\Clients\\StartMenuInternet"; |
| const wchar_t* ShellUtil::kRegClasses = L"Software\\Classes"; |
| const wchar_t* ShellUtil::kRegRegisteredApplications = |
| L"Software\\RegisteredApplications"; |
| const wchar_t* ShellUtil::kRegVistaUrlPrefs = |
| L"Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\" |
| L"http\\UserChoice"; |
| const wchar_t* ShellUtil::kAppPathsRegistryKey = |
| L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths"; |
| const wchar_t* ShellUtil::kAppPathsRegistryPathName = L"Path"; |
| |
| #if defined(GOOGLE_CHROME_BUILD) |
| const wchar_t* ShellUtil::kChromeHTMLProgId = L"ChromeHTML"; |
| const wchar_t* ShellUtil::kChromeHTMLProgIdDesc = L"Chrome HTML Document"; |
| #else |
| const wchar_t* ShellUtil::kChromeHTMLProgId = L"ChromiumHTML"; |
| const wchar_t* ShellUtil::kChromeHTMLProgIdDesc = L"Chromium HTML Document"; |
| #endif |
| |
| 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::kRegUrlProtocol = L"URL Protocol"; |
| |
| bool ShellUtil::AdminNeededForRegistryCleanup(const std::wstring& suffix) { |
| bool cleanup_needed = false; |
| std::list<RegistryEntry*> entries; |
| STLElementDeleter<std::list<RegistryEntry*>> entries_deleter(&entries); |
| RegistryEntry::GetProgIdEntries(L"chrome.exe", suffix, &entries); |
| RegistryEntry::GetSystemEntries(L"chrome.exe", suffix, &entries); |
| for (std::list<RegistryEntry*>::const_iterator itr = entries.begin(); |
| itr != entries.end() && !cleanup_needed; ++itr) { |
| cleanup_needed = (*itr)->NameExistsInHKLM(); |
| } |
| return cleanup_needed; |
| } |
| |
| bool ShellUtil::CreateChromeDesktopShortcut(const std::wstring& chrome_exe, |
| const std::wstring& description, |
| int shell_change, bool alternate, |
| bool create_new) { |
| std::wstring shortcut_name; |
| if (!ShellUtil::GetChromeShortcutName(&shortcut_name, alternate)) |
| return false; |
| |
| bool ret = true; |
| if (shell_change & ShellUtil::CURRENT_USER) { |
| std::wstring shortcut_path; |
| if (ShellUtil::GetDesktopPath(false, &shortcut_path)) { |
| file_util::AppendToPath(&shortcut_path, shortcut_name); |
| ret = ShellUtil::UpdateChromeShortcut(chrome_exe, shortcut_path, |
| description, create_new); |
| } else { |
| ret = false; |
| } |
| } |
| if (shell_change & ShellUtil::SYSTEM_LEVEL) { |
| std::wstring shortcut_path; |
| if (ShellUtil::GetDesktopPath(true, &shortcut_path)) { |
| file_util::AppendToPath(&shortcut_path, shortcut_name); |
| // Note we need to call the create operation and then AND the result |
| // with the create operation of user level shortcut. |
| ret = ShellUtil::UpdateChromeShortcut(chrome_exe, shortcut_path, |
| description, create_new) && ret; |
| } else { |
| ret = false; |
| } |
| } |
| return ret; |
| } |
| |
| bool ShellUtil::CreateChromeQuickLaunchShortcut(const std::wstring& chrome_exe, |
| int shell_change, |
| bool create_new) { |
| std::wstring shortcut_name; |
| if (!ShellUtil::GetChromeShortcutName(&shortcut_name, false)) |
| return false; |
| |
| bool ret = true; |
| // First create shortcut for the current user. |
| if (shell_change & ShellUtil::CURRENT_USER) { |
| std::wstring user_ql_path; |
| if (ShellUtil::GetQuickLaunchPath(false, &user_ql_path)) { |
| file_util::AppendToPath(&user_ql_path, shortcut_name); |
| ret = ShellUtil::UpdateChromeShortcut(chrome_exe, user_ql_path, |
| L"", create_new); |
| } else { |
| ret = false; |
| } |
| } |
| |
| // Add a shortcut to Default User's profile so that all new user profiles |
| // get it. |
| if (shell_change & ShellUtil::SYSTEM_LEVEL) { |
| std::wstring default_ql_path; |
| if (ShellUtil::GetQuickLaunchPath(true, &default_ql_path)) { |
| file_util::AppendToPath(&default_ql_path, shortcut_name); |
| ret = ShellUtil::UpdateChromeShortcut(chrome_exe, default_ql_path, |
| L"", create_new) && ret; |
| } else { |
| ret = false; |
| } |
| } |
| |
| return ret; |
| } |
| |
| std::wstring ShellUtil::GetChromeIcon(const std::wstring& chrome_exe) { |
| BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
| std::wstring chrome_icon(chrome_exe); |
| chrome_icon.append(L","); |
| chrome_icon.append(base::IntToString16(dist->GetIconIndex())); |
| return chrome_icon; |
| } |
| |
| std::wstring ShellUtil::GetChromeShellOpenCmd(const std::wstring& chrome_exe) { |
| return L"\"" + chrome_exe + L"\" -- \"%1\""; |
| } |
| |
| bool ShellUtil::GetChromeShortcutName(std::wstring* shortcut, bool alternate) { |
| BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
| shortcut->assign(alternate ? dist->GetAlternateApplicationName() : |
| dist->GetAppShortCutName()); |
| shortcut->append(L".lnk"); |
| return true; |
| } |
| |
| bool ShellUtil::GetDesktopPath(bool system_level, std::wstring* path) { |
| wchar_t desktop[MAX_PATH]; |
| int dir = system_level ? CSIDL_COMMON_DESKTOPDIRECTORY : |
| CSIDL_DESKTOPDIRECTORY; |
| if (FAILED(SHGetFolderPath(NULL, dir, NULL, SHGFP_TYPE_CURRENT, desktop))) |
| return false; |
| *path = desktop; |
| return true; |
| } |
| |
| bool ShellUtil::GetQuickLaunchPath(bool system_level, std::wstring* path) { |
| static const wchar_t* kQuickLaunchPath = |
| L"Microsoft\\Internet Explorer\\Quick Launch"; |
| wchar_t qlaunch[MAX_PATH]; |
| if (system_level) { |
| // We are accessing GetDefaultUserProfileDirectory this way so that we do |
| // not have to declare dependency to Userenv.lib for chrome.exe |
| typedef BOOL (WINAPI *PROFILE_FUNC)(LPWSTR, LPDWORD); |
| HMODULE module = LoadLibrary(L"Userenv.dll"); |
| PROFILE_FUNC p = reinterpret_cast<PROFILE_FUNC>(GetProcAddress(module, |
| "GetDefaultUserProfileDirectoryW")); |
| DWORD size = _countof(qlaunch); |
| if ((p == NULL) || ((p)(qlaunch, &size) != TRUE)) |
| return false; |
| *path = qlaunch; |
| if (base::win::GetVersion() >= base::win::VERSION_VISTA) { |
| file_util::AppendToPath(path, L"AppData\\Roaming"); |
| } else { |
| file_util::AppendToPath(path, L"Application Data"); |
| } |
| } else { |
| if (FAILED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, |
| SHGFP_TYPE_CURRENT, qlaunch))) |
| return false; |
| *path = qlaunch; |
| } |
| file_util::AppendToPath(path, kQuickLaunchPath); |
| return true; |
| } |
| |
| void ShellUtil::GetRegisteredBrowsers(std::map<std::wstring, |
| std::wstring>* browsers) { |
| std::wstring base_key(ShellUtil::kRegStartMenuInternet); |
| HKEY root = HKEY_LOCAL_MACHINE; |
| for (base::win::RegistryKeyIterator iter(root, base_key.c_str()); |
| iter.Valid(); ++iter) { |
| std::wstring key = base_key + L"\\" + iter.Name(); |
| RegKey capabilities(root, (key + L"\\Capabilities").c_str(), KEY_READ); |
| std::wstring name; |
| if (!capabilities.Valid() || |
| !capabilities.ReadValue(L"ApplicationName", &name)) { |
| RegKey base_key(root, key.c_str(), KEY_READ); |
| if (!base_key.ReadValue(L"", &name)) |
| continue; |
| } |
| RegKey install_info(root, (key + L"\\InstallInfo").c_str(), KEY_READ); |
| std::wstring command; |
| if (!install_info.Valid() || |
| !install_info.ReadValue(L"ReinstallCommand", &command)) |
| continue; |
| BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
| if (!name.empty() && !command.empty() && |
| name.find(dist->GetApplicationName()) == std::wstring::npos) |
| (*browsers)[name] = command; |
| } |
| } |
| |
| bool ShellUtil::GetUserSpecificDefaultBrowserSuffix(std::wstring* entry) { |
| wchar_t user_name[256]; |
| DWORD size = _countof(user_name); |
| if (::GetUserName(user_name, &size) == 0) |
| return false; |
| entry->assign(L"."); |
| entry->append(user_name); |
| |
| std::wstring start_menu_entry(ShellUtil::kRegStartMenuInternet); |
| BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
| start_menu_entry.append(L"\\" + dist->GetApplicationName() + *entry); |
| RegKey key(HKEY_LOCAL_MACHINE, start_menu_entry.c_str(), KEY_READ); |
| return key.Valid(); |
| } |
| |
| bool ShellUtil::MakeChromeDefault(int shell_change, |
| const std::wstring& chrome_exe, |
| bool elevate_if_not_admin) { |
| if (!BrowserDistribution::GetDistribution()->CanSetAsDefault()) |
| return false; |
| |
| ShellUtil::RegisterChromeBrowser(chrome_exe, L"", elevate_if_not_admin); |
| |
| bool ret = true; |
| // First use the new "recommended" way on Vista to make Chrome default |
| // browser. |
| if (base::win::GetVersion() >= base::win::VERSION_VISTA) { |
| LOG(INFO) << "Registering Chrome as default browser on Vista."; |
| IApplicationAssociationRegistration* pAAR; |
| HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration, |
| NULL, CLSCTX_INPROC, __uuidof(IApplicationAssociationRegistration), |
| (void**)&pAAR); |
| if (SUCCEEDED(hr)) { |
| BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
| std::wstring app_name = dist->GetApplicationName(); |
| std::wstring suffix; |
| if (ShellUtil::GetUserSpecificDefaultBrowserSuffix(&suffix)) |
| app_name += suffix; |
| |
| hr = pAAR->SetAppAsDefaultAll(app_name.c_str()); |
| pAAR->Release(); |
| } |
| if (!SUCCEEDED(hr)) { |
| ret = false; |
| LOG(ERROR) << "Could not make Chrome default browser."; |
| } |
| } |
| |
| // Now use the old way to associate Chrome with supported protocols and file |
| // associations. 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(&suffix)) |
| suffix = L""; |
| RegistryEntry::GetUserEntries(chrome_exe, suffix, &entries); |
| // Change the default browser for current user. |
| if ((shell_change & ShellUtil::CURRENT_USER) && |
| !AddRegistryEntries(HKEY_CURRENT_USER, entries)) |
| ret = false; |
| |
| // Chrome as default browser at system level. |
| if ((shell_change & ShellUtil::SYSTEM_LEVEL) && |
| !AddRegistryEntries(HKEY_LOCAL_MACHINE, entries)) |
| ret = false; |
| |
| // Send Windows notification event so that it can update icons for |
| // file associations. |
| SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL); |
| return ret; |
| } |
| |
| bool ShellUtil::RegisterChromeBrowser(const std::wstring& chrome_exe, |
| const std::wstring& unique_suffix, |
| bool elevate_if_not_admin) { |
| if (!BrowserDistribution::GetDistribution()->CanSetAsDefault()) |
| return false; |
| |
| // First 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(&suffix) && |
| !AnotherUserHasDefaultBrowser(chrome_exe)) { |
| suffix = L""; |
| } |
| |
| // Check if Chromium is already registered with this suffix. |
| if (IsChromeRegistered(chrome_exe, suffix)) |
| return true; |
| |
| // If user is an admin try to register and return the status. |
| if (IsUserAnAdmin()) { |
| std::list<RegistryEntry*> entries; |
| STLElementDeleter<std::list<RegistryEntry*>> entries_deleter(&entries); |
| RegistryEntry::GetProgIdEntries(chrome_exe, suffix, &entries); |
| RegistryEntry::GetSystemEntries(chrome_exe, suffix, &entries); |
| return AddRegistryEntries(HKEY_LOCAL_MACHINE, entries); |
| } |
| |
| // 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(chrome_exe, suffix)) |
| return true; |
| |
| // If we got to this point then all we can do is create ProgIds under HKCU |
| // on XP as well as Vista. |
| std::list<RegistryEntry*> entries; |
| STLElementDeleter<std::list<RegistryEntry*>> entries_deleter(&entries); |
| RegistryEntry::GetProgIdEntries(chrome_exe, L"", &entries); |
| return AddRegistryEntries(HKEY_CURRENT_USER, entries); |
| } |
| |
| bool ShellUtil::RemoveChromeDesktopShortcut(int shell_change, bool alternate) { |
| std::wstring shortcut_name; |
| if (!ShellUtil::GetChromeShortcutName(&shortcut_name, alternate)) |
| return false; |
| |
| bool ret = true; |
| if (shell_change & ShellUtil::CURRENT_USER) { |
| std::wstring shortcut_path; |
| if (ShellUtil::GetDesktopPath(false, &shortcut_path)) { |
| file_util::AppendToPath(&shortcut_path, shortcut_name); |
| ret = file_util::Delete(shortcut_path, false); |
| } else { |
| ret = false; |
| } |
| } |
| |
| if (shell_change & ShellUtil::SYSTEM_LEVEL) { |
| std::wstring shortcut_path; |
| if (ShellUtil::GetDesktopPath(true, &shortcut_path)) { |
| file_util::AppendToPath(&shortcut_path, shortcut_name); |
| ret = file_util::Delete(shortcut_path, false) && ret; |
| } else { |
| ret = false; |
| } |
| } |
| return ret; |
| } |
| |
| bool ShellUtil::RemoveChromeQuickLaunchShortcut(int shell_change) { |
| std::wstring shortcut_name; |
| if (!ShellUtil::GetChromeShortcutName(&shortcut_name, false)) |
| return false; |
| |
| bool ret = true; |
| // First remove shortcut for the current user. |
| if (shell_change & ShellUtil::CURRENT_USER) { |
| std::wstring user_ql_path; |
| if (ShellUtil::GetQuickLaunchPath(false, &user_ql_path)) { |
| file_util::AppendToPath(&user_ql_path, shortcut_name); |
| ret = file_util::Delete(user_ql_path, false); |
| } else { |
| ret = false; |
| } |
| } |
| |
| // Delete shortcut in Default User's profile |
| if (shell_change & ShellUtil::SYSTEM_LEVEL) { |
| std::wstring default_ql_path; |
| if (ShellUtil::GetQuickLaunchPath(true, &default_ql_path)) { |
| file_util::AppendToPath(&default_ql_path, shortcut_name); |
| ret = file_util::Delete(default_ql_path, false) && ret; |
| } else { |
| ret = false; |
| } |
| } |
| |
| return ret; |
| } |
| |
| bool ShellUtil::UpdateChromeShortcut(const std::wstring& chrome_exe, |
| const std::wstring& shortcut, |
| const std::wstring& description, |
| bool create_new) { |
| BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
| std::wstring chrome_path = file_util::GetDirectoryFromPath(chrome_exe); |
| |
| FilePath prefs_path(chrome_path); |
| prefs_path = prefs_path.AppendASCII(installer_util::kDefaultMasterPrefs); |
| scoped_ptr<DictionaryValue> prefs( |
| installer_util::ParseDistributionPreferences(prefs_path)); |
| int icon_index = dist->GetIconIndex(); |
| installer_util::GetDistroIntegerPreference(prefs.get(), |
| installer_util::master_preferences::kChromeShortcutIconIndex, |
| &icon_index); |
| if (create_new) { |
| return file_util::CreateShortcutLink( |
| chrome_exe.c_str(), // target |
| shortcut.c_str(), // shortcut |
| chrome_path.c_str(), // working dir |
| NULL, // arguments |
| description.c_str(), // description |
| chrome_exe.c_str(), // icon file |
| icon_index, // icon index |
| dist->GetBrowserAppId().c_str()); // app id |
| } else { |
| return file_util::UpdateShortcutLink( |
| chrome_exe.c_str(), // target |
| shortcut.c_str(), // shortcut |
| chrome_path.c_str(), // working dir |
| NULL, // arguments |
| description.c_str(), // description |
| chrome_exe.c_str(), // icon file |
| icon_index, // icon index |
| dist->GetBrowserAppId().c_str()); // app id |
| } |
| } |