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 113b4793ce0c1a6d38d992d3041d805df6c93dba..a82d9ae056ee79cdfca531792246ffd5f3e0668e 100644 |
| --- a/chrome/installer/util/shell_util.cc |
| +++ b/chrome/installer/util/shell_util.cc |
| @@ -46,6 +46,8 @@ using base::win::RegKey; |
| namespace { |
| +typedef ShellUtil::ChromeShortcutProperties ChromeShortcutProperties; |
| + |
| // An enum used to tell QuickIsChromeRegistered() which level of registration |
| // the caller wants to confirm. |
| enum RegistrationConfirmationLevel { |
| @@ -937,6 +939,98 @@ bool RegisterChromeAsDefaultProtocolClientForXP(BrowserDistribution* dist, |
| return true; |
| } |
| +// Returns |properties.shortcut_name| if the property is set, otherwise it |
| +// returns dist->GetAppShortcutName(). In any case, it makes sure the |
| +// return value is suffixed with ".lnk". |
| +string16 ExtractShortcutNameFromProperties( |
| + BrowserDistribution* dist, |
| + const ChromeShortcutProperties& properties) { |
| + DCHECK(dist); |
| + string16 shortcut_name; |
| + if (properties.options & ChromeShortcutProperties::PROPERTIES_SHORTCUT_NAME) |
| + shortcut_name = properties.shortcut_name; |
| + else |
| + shortcut_name = dist->GetAppShortCutName(); |
| + |
| + static const wchar_t lnk_ext[] = L".lnk"; |
|
grt (UTC plus 2)
2012/10/02 13:22:30
although the style guide doesn't call it out, i th
gab
2012/10/03 15:14:59
Adding L".lnk" in a bunch of places has been buggi
|
| + if (!EndsWith(shortcut_name, lnk_ext, false)) |
| + shortcut_name.append(lnk_ext); |
| + |
| + return shortcut_name; |
| +} |
| + |
| +// Returns a ShortcutProperties struct containing the properties to set on the |
| +// shortcut based on the provided ChromeShortcutProperties. If |operation| is |
| +// to create, some properties (e.g. icon, app_id) might be given a default if |
| +// not set in |properties| (see individual ChromeShortcutProperties for |
| +// details). |
| +base::win::ShortcutProperties GetShortcutPropertiesFromChromeShortcutProperties( |
| + BrowserDistribution* dist, |
| + const ChromeShortcutProperties& properties, |
| + base::win::ShortcutOperation operation) { |
| + bool create = (operation == base::win::SHORTCUT_CREATE_ALWAYS || |
| + operation == base::win::SHORTCUT_REPLACE_EXISTING); |
| + // |target| is mandatory when creating the shortcut. |
| + DCHECK(!create || |
| + (properties.options & ChromeShortcutProperties::PROPERTIES_TARGET)); |
| + |
| + base::win::ShortcutProperties shortcut_properties; |
| + |
| + bool target_is_chrome_exe = false; |
|
grt (UTC plus 2)
2012/10/02 13:22:30
i wonder if there's a better way to handle default
gab
2012/10/03 15:14:59
As discussed the target is now always chrome.exe w
|
| + if (properties.options & ChromeShortcutProperties::PROPERTIES_TARGET) { |
| + shortcut_properties.set_target(properties.target); |
| + DCHECK(!properties.target.DirName().empty()); |
| + shortcut_properties.set_working_dir(properties.target.DirName()); |
| + |
| + if (properties.target.BaseName() == FilePath(installer::kChromeExe)) |
|
grt (UTC plus 2)
2012/10/02 13:22:30
BaseName().value() == installer.kChromeExe to avoi
gab
2012/10/03 15:14:59
I also debated doing that and decided not to given
|
| + target_is_chrome_exe = true; |
| + } |
| + |
| + if (properties.options & ChromeShortcutProperties::PROPERTIES_ARGUMENTS) |
| + shortcut_properties.set_arguments(properties.arguments); |
| + |
| + if (properties.options & ChromeShortcutProperties::PROPERTIES_DESCRIPTION) |
| + shortcut_properties.set_description(properties.description); |
| + else if (create && target_is_chrome_exe) |
| + shortcut_properties.set_description(dist->GetAppDescription()); |
| + |
| + if (properties.options & ChromeShortcutProperties::PROPERTIES_ICON) { |
| + shortcut_properties.set_icon(properties.icon, 0); |
|
grt (UTC plus 2)
2012/10/02 13:22:30
the icon index can't be set by users of ChromeShor
gab
2012/10/03 15:14:59
No current caller required it, i.e. either callers
|
| + } else if (create) { |
| + // Set the icon to |properties.target| by default when creating a shortcut. |
| + // If |properties.target| is chrome.exe, set |icon_index| as per the |
| + // master_preferences if specified or as per the default icon index for this |
| + // distribution. |
| + int icon_index = 0; |
| + if (target_is_chrome_exe) { |
| + installer::MasterPreferences prefs( |
|
grt (UTC plus 2)
2012/10/02 13:22:30
this scares me a little bit. let's talk about it.
gab
2012/10/03 15:14:59
Yea it's ugly, just copying the old behavior from
|
| + properties.target.DirName().AppendASCII( |
| + installer::kDefaultMasterPrefs)); |
| + if (!prefs.GetInt(installer::master_preferences::kChromeShortcutIconIndex, |
| + &icon_index)) { |
| + icon_index = dist->GetIconIndex(); |
| + } |
| + } |
| + shortcut_properties.set_icon(properties.target, icon_index); |
| + } |
| + |
| + if (properties.options & ChromeShortcutProperties::PROPERTIES_APP_ID) { |
| + shortcut_properties.set_app_id(properties.app_id); |
| + } else if (create && target_is_chrome_exe) { |
| + // Set the AppUserModelId to this distribution's AppId by default when |
| + // creating the shortcut and the target is chrome.exe. |
| + bool is_per_user_install = |
| + InstallUtil::IsPerUserInstall(properties.target.value().c_str()); |
| + shortcut_properties.set_app_id( |
| + ShellUtil::GetBrowserModelId(dist, is_per_user_install)); |
| + } |
| + |
| + if (properties.options & ChromeShortcutProperties::PROPERTIES_DUAL_MODE) |
| + shortcut_properties.set_dual_mode(properties.dual_mode); |
| + |
| + return shortcut_properties; |
| +} |
| + |
| } // namespace |
| const wchar_t* ShellUtil::kRegDefaultIcon = L"\\DefaultIcon"; |
| @@ -992,100 +1086,113 @@ bool ShellUtil::QuickIsChromeRegisteredInHKLM(BrowserDistribution* dist, |
| CONFIRM_SHELL_REGISTRATION_IN_HKLM); |
| } |
| -bool ShellUtil::CreateChromeDesktopShortcut(BrowserDistribution* dist, |
| - const string16& chrome_exe, |
| - const string16& description, |
| - const string16& appended_name, |
| - const string16& arguments, |
| - const string16& icon_path, |
| - int icon_index, |
| - ShellChange shell_change, |
| - uint32 options) { |
| - string16 shortcut_name; |
| - bool alternate = (options & ShellUtil::SHORTCUT_ALTERNATE) != 0; |
| - if (!ShellUtil::GetChromeShortcutName(dist, alternate, appended_name, |
| - &shortcut_name)) |
| +bool ShellUtil::GetShortcutPath(ChromeShortcutLocation location, |
| + BrowserDistribution* dist, |
|
robertshield
2012/10/02 13:08:04
this doesn't use the dist parameter
gab
2012/10/03 15:14:59
Oops, I added this with the intent of removing Get
|
| + bool system_level, |
| + FilePath* path) { |
| + int dir_key = -1; |
| + bool add_folder_for_dist = false; |
| + switch (location) { |
| + case SHORTCUT_DESKTOP: |
| + dir_key = system_level ? base::DIR_COMMON_DESKTOP : |
| + base::DIR_USER_DESKTOP; |
| + break; |
| + case SHORTCUT_QUICK_LAUNCH: |
| + dir_key = system_level ? base::DIR_DEFAULT_USER_QUICK_LAUNCH : |
| + base::DIR_USER_QUICK_LAUNCH; |
| + break; |
| + case SHORTCUT_START_MENU: |
| + dir_key = system_level ? base::DIR_COMMON_START_MENU : |
| + base::DIR_START_MENU; |
| + add_folder_for_dist = true; |
| + break; |
| + default: |
| + NOTREACHED(); |
| + return false; |
| + } |
| + |
| + if (dir_key == -1 || !PathService::Get(dir_key, path) || path->empty()) { |
| + NOTREACHED() << dir_key; |
| return false; |
| + } |
| - bool ret = false; |
| - if (shell_change == ShellUtil::CURRENT_USER) { |
| - FilePath shortcut_path; |
| - // We do not want to create a desktop shortcut to Chrome in the current |
| - // user's desktop folder if there is already one in the "All Users" |
| - // desktop folder. |
| - bool got_system_desktop = ShellUtil::GetDesktopPath(true, &shortcut_path); |
| - FilePath shortcut = shortcut_path.Append(shortcut_name); |
| - if (!got_system_desktop || !file_util::PathExists(shortcut)) { |
| - // Either we couldn't query the "All Users" Desktop folder or there's |
| - // nothing in it, so let's continue. |
| - if (ShellUtil::GetDesktopPath(false, &shortcut_path)) { |
| - shortcut = shortcut_path.Append(shortcut_name); |
| - ret = ShellUtil::UpdateChromeShortcut(dist, |
| - chrome_exe, |
| - shortcut.value(), |
| - arguments, |
| - description, |
| - icon_path, |
| - icon_index, |
| - options); |
| - } |
| - } |
| - } else if (shell_change == ShellUtil::SYSTEM_LEVEL) { |
| - FilePath shortcut_path; |
| - if (ShellUtil::GetDesktopPath(true, &shortcut_path)) { |
| - FilePath shortcut = shortcut_path.Append(shortcut_name); |
| - ret = ShellUtil::UpdateChromeShortcut(dist, |
| - chrome_exe, |
| - shortcut.value(), |
| - arguments, |
| - description, |
| - icon_path, |
| - icon_index, |
| - options); |
| - } |
| - } else { |
| - NOTREACHED(); |
| + if (add_folder_for_dist) { |
| + BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
| + *path = path->Append(dist->GetAppShortCutName()); |
| } |
| - return ret; |
| + |
| + return true; |
| } |
| -bool ShellUtil::CreateChromeQuickLaunchShortcut(BrowserDistribution* dist, |
| - const string16& chrome_exe, |
| - int shell_change, |
| - uint32 options) { |
| - string16 shortcut_name; |
| - if (!ShellUtil::GetChromeShortcutName(dist, false, L"", &shortcut_name)) |
| +bool ShellUtil::CreateOrUpdateChromeShortcut( |
| + ShellUtil::ChromeShortcutLocation location, |
| + BrowserDistribution* dist, |
| + const ChromeShortcutProperties& properties, |
| + ChromeShortcutOperation operation) { |
| + DCHECK(dist); |
| + // |pin_to_taskbar| is only acknowledged when first creating the shortcut. |
| + DCHECK(!properties.pin_to_taskbar || |
| + operation == base::win::SHORTCUT_CREATE_ALWAYS); |
| + |
| + FilePath user_shortcut_path; |
| + FilePath system_shortcut_path; |
| + if (!GetShortcutPath(location, dist, false, &user_shortcut_path) || |
| + !GetShortcutPath(location, dist, true, &system_shortcut_path) || |
| + user_shortcut_path.empty() || |
| + system_shortcut_path.empty()) { |
| + NOTREACHED(); |
| return false; |
| + } |
| - bool ret = true; |
| - // First create shortcut for the current user. |
| - if (shell_change & ShellUtil::CURRENT_USER) { |
| - FilePath user_ql_path; |
| - if (ShellUtil::GetQuickLaunchPath(false, &user_ql_path)) { |
| - user_ql_path = user_ql_path.Append(shortcut_name); |
| - ret = ShellUtil::UpdateChromeShortcut(dist, chrome_exe, |
| - user_ql_path.value(), |
| - L"", L"", chrome_exe, |
| - dist->GetIconIndex(), |
| - options); |
| - } else { |
| - ret = false; |
| - } |
| + string16 shortcut_name(ExtractShortcutNameFromProperties(dist, properties)); |
| + user_shortcut_path = user_shortcut_path.Append(shortcut_name); |
| + system_shortcut_path = system_shortcut_path.Append(shortcut_name); |
| + |
| + FilePath *chosen_path; |
| + if (properties.system_level) { |
| + // Install the system-level shortcut if requested. |
| + chosen_path = &system_shortcut_path; |
| + } else if (operation != SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL || |
| + !file_util::PathExists(system_shortcut_path)){ |
| + // Otherwise install the user-level shortcut, unless the system-level |
| + // variant of this shortcut is present on the machine and |operation| states |
| + // not to create a user-level shortcut in that case. |
| + chosen_path = &user_shortcut_path; |
| + } else { |
| + // Do not install anything if we are told to install a user-level shortcut, |
| + // but the system-level variant of that shortcut is present. |
| + chosen_path = &FilePath(); |
| } |
| - // Add a shortcut to Default User's profile so that all new user profiles |
| - // get it. |
| - if (shell_change & ShellUtil::SYSTEM_LEVEL) { |
| - FilePath default_ql_path; |
| - if (ShellUtil::GetQuickLaunchPath(true, &default_ql_path)) { |
| - default_ql_path = default_ql_path.Append(shortcut_name); |
| - ret = ShellUtil::UpdateChromeShortcut(dist, chrome_exe, |
| - default_ql_path.value(), |
| - L"", L"", chrome_exe, |
| - dist->GetIconIndex(), |
| - options) && ret; |
| - } else { |
| - ret = false; |
| + // No shortcut needs to be created/updated. |
| + if (chosen_path->empty()) |
| + return true; |
| + |
| + // Make sure the parent directories exist when creating the shortcut. |
| + if (operation == SHORTCUT_CREATE_ALWAYS && |
| + !file_util::CreateDirectory(chosen_path->DirName())) { |
| + NOTREACHED(); |
| + return false; |
| + } |
| + |
| + base::win::ShortcutOperation shortcut_operation = |
| + (operation == SHORTCUT_UPDATE_EXISTING ? |
| + base::win::SHORTCUT_UPDATE_EXISTING : |
| + (operation == SHORTCUT_REPLACE_EXISTING ? |
| + base::win::SHORTCUT_REPLACE_EXISTING : |
| + base::win::SHORTCUT_CREATE_ALWAYS)); |
| + base::win::ShortcutProperties shortcut_properties( |
| + GetShortcutPropertiesFromChromeShortcutProperties(dist, properties, |
| + shortcut_operation)); |
| + bool ret = base::win::CreateOrUpdateShortcutLink( |
| + *chosen_path, shortcut_properties, shortcut_operation); |
| + |
| + if (ret && operation == SHORTCUT_CREATE_ALWAYS && properties.pin_to_taskbar && |
| + base::win::GetVersion() >= base::win::VERSION_WIN7) { |
| + ret = base::win::TaskbarPinShortcutLink(chosen_path->value().c_str()); |
| + if (!ret) { |
| + LOG(ERROR) << "The shorcut at " << chosen_path->value() |
| + << " was created, but the taskbar pin failed."; |
| } |
| } |
| @@ -1108,33 +1215,6 @@ string16 ShellUtil::GetChromeDelegateCommand(const string16& chrome_exe) { |
| return L"\"" + chrome_exe + L"\" -- %*"; |
| } |
| -bool ShellUtil::GetChromeShortcutName(BrowserDistribution* dist, |
| - bool alternate, |
| - const string16& appended_name, |
| - string16* shortcut) { |
| - shortcut->assign(alternate ? dist->GetAlternateApplicationName() : |
| - dist->GetAppShortCutName()); |
| - if (!appended_name.empty()) { |
| - shortcut->append(L" ("); |
| - shortcut->append(appended_name); |
| - shortcut->append(L")"); |
| - } |
| - shortcut->append(L".lnk"); |
| - return true; |
| -} |
| - |
| -bool ShellUtil::GetDesktopPath(bool system_level, FilePath* path) { |
| - int dir_key = system_level ? base::DIR_COMMON_DESKTOP : |
| - base::DIR_USER_DESKTOP; |
| - return PathService::Get(dir_key, path); |
| -} |
| - |
| -bool ShellUtil::GetQuickLaunchPath(bool system_level, FilePath* path) { |
| - int dir_key = system_level ? base::DIR_DEFAULT_USER_QUICK_LAUNCH : |
| - base::DIR_USER_QUICK_LAUNCH; |
| - return PathService::Get(dir_key, path); |
| -} |
| - |
| void ShellUtil::GetRegisteredBrowsers( |
| BrowserDistribution* dist, |
| std::map<string16, string16>* browsers) { |
| @@ -1551,88 +1631,32 @@ bool ShellUtil::RegisterChromeForProtocol(BrowserDistribution* dist, |
| } |
| } |
| -bool ShellUtil::RemoveChromeDesktopShortcut(BrowserDistribution* dist, |
| - int shell_change, uint32 options) { |
| - // Only SHORTCUT_ALTERNATE is a valid option for this function. |
| - DCHECK(!options || options == ShellUtil::SHORTCUT_ALTERNATE); |
| +bool ShellUtil::RemoveChromeShortcut( |
| + ChromeShortcutLocation location, |
| + BrowserDistribution* dist, |
| + const ChromeShortcutProperties& properties) { |
| + bool delete_folder = (location == SHORTCUT_START_MENU); |
| - string16 shortcut_name; |
| - bool alternate = (options & ShellUtil::SHORTCUT_ALTERNATE) != 0; |
| - if (!ShellUtil::GetChromeShortcutName(dist, alternate, L"", |
| - &shortcut_name)) |
| + FilePath shortcut_folder; |
| + if (!GetShortcutPath(location, dist, properties.system_level, |
| + &shortcut_folder) || |
| + shortcut_folder.empty()) { |
| + NOTREACHED(); |
| return false; |
| - |
| - bool ret = true; |
| - if (shell_change & ShellUtil::CURRENT_USER) { |
| - FilePath shortcut_path; |
| - if (ShellUtil::GetDesktopPath(false, &shortcut_path)) { |
| - FilePath shortcut = shortcut_path.Append(shortcut_name); |
| - ret = file_util::Delete(shortcut, false); |
| - } else { |
| - ret = false; |
| - } |
| - } |
| - |
| - if (shell_change & ShellUtil::SYSTEM_LEVEL) { |
| - FilePath shortcut_path; |
| - if (ShellUtil::GetDesktopPath(true, &shortcut_path)) { |
| - FilePath shortcut = shortcut_path.Append(shortcut_name); |
| - ret = file_util::Delete(shortcut, false) && ret; |
| - } else { |
| - ret = false; |
| - } |
| - } |
| - return ret; |
| -} |
| - |
| -bool ShellUtil::RemoveChromeDesktopShortcutsWithAppendedNames( |
| - const std::vector<string16>& appended_names) { |
| - FilePath shortcut_path; |
| - bool ret = true; |
| - if (ShellUtil::GetDesktopPath(false, &shortcut_path)) { |
| - for (std::vector<string16>::const_iterator it = |
| - appended_names.begin(); |
| - it != appended_names.end(); |
| - ++it) { |
| - FilePath delete_shortcut = shortcut_path.Append(*it); |
| - ret = ret && file_util::Delete(delete_shortcut, false); |
| - } |
| - } else { |
| - ret = false; |
| } |
| - return ret; |
| -} |
| -bool ShellUtil::RemoveChromeQuickLaunchShortcut(BrowserDistribution* dist, |
| - int shell_change) { |
| - string16 shortcut_name; |
| - if (!ShellUtil::GetChromeShortcutName(dist, false, L"", &shortcut_name)) |
| - return false; |
| + FilePath shortcut_path(shortcut_folder.Append( |
| + ExtractShortcutNameFromProperties(dist, properties))); |
| - bool ret = true; |
| - // First remove shortcut for the current user. |
| - if (shell_change & ShellUtil::CURRENT_USER) { |
| - FilePath user_ql_path; |
| - if (ShellUtil::GetQuickLaunchPath(false, &user_ql_path)) { |
| - user_ql_path = user_ql_path.Append(shortcut_name); |
| - ret = file_util::Delete(user_ql_path, false); |
| - } else { |
| - ret = false; |
| - } |
| - } |
| + // Unpin the shortcut if it was ever pinned by the user or the installer. |
| + VLOG(1) << "Trying to unpin " << shortcut_path.value(); |
| + if (!base::win::TaskbarUnpinShortcutLink(shortcut_path.value().c_str())) |
| + VLOG(1) << shortcut_path.value() << " wasn't pinned (or the unpin failed)."; |
| - // Delete shortcut in Default User's profile |
| - if (shell_change & ShellUtil::SYSTEM_LEVEL) { |
| - FilePath default_ql_path; |
| - if (ShellUtil::GetQuickLaunchPath(true, &default_ql_path)) { |
| - default_ql_path = default_ql_path.Append(shortcut_name); |
| - ret = file_util::Delete(default_ql_path, false) && ret; |
| - } else { |
| - ret = false; |
| - } |
| - } |
| - |
| - return ret; |
| + if (delete_folder) |
| + return file_util::Delete(shortcut_folder, true); |
| + else |
| + return file_util::Delete(shortcut_path, false); |
| } |
| void ShellUtil::RemoveChromeStartScreenShortcuts(BrowserDistribution* dist, |
| @@ -1663,46 +1687,6 @@ void ShellUtil::RemoveChromeStartScreenShortcuts(BrowserDistribution* dist, |
| } |
| } |
| -bool ShellUtil::UpdateChromeShortcut(BrowserDistribution* dist, |
| - const string16& chrome_exe, |
| - const string16& shortcut, |
| - const string16& arguments, |
| - const string16& description, |
| - const string16& icon_path, |
| - int icon_index, |
| - uint32 options) { |
| - const FilePath chrome_path(FilePath(chrome_exe).DirName()); |
| - |
| - installer::MasterPreferences prefs( |
| - chrome_path.AppendASCII(installer::kDefaultMasterPrefs)); |
| - if (FilePath::CompareEqualIgnoreCase(icon_path, chrome_exe)) { |
| - prefs.GetInt(installer::master_preferences::kChromeShortcutIconIndex, |
| - &icon_index); |
| - } |
| - |
| - const string16 app_id( |
| - GetBrowserModelId(dist, |
| - InstallUtil::IsPerUserInstall(chrome_exe.c_str()))); |
| - const bool is_dual_mode = ((options & ShellUtil::SHORTCUT_DUAL_MODE) != 0); |
| - const base::win::ShortcutOperation operation = |
| - (options & ShellUtil::SHORTCUT_CREATE_ALWAYS) != 0 ? |
| - base::win::SHORTCUT_CREATE_ALWAYS : |
| - base::win::SHORTCUT_UPDATE_EXISTING; |
| - |
| - // TODO(gab): The shell_util interface will also be refactored in an upcoming |
| - // CL to use a ShortcutProperties like interface for its shortcut methods. |
| - base::win::ShortcutProperties shortcut_properties; |
| - shortcut_properties.set_target(FilePath(chrome_exe)); |
| - shortcut_properties.set_working_dir(chrome_path); |
| - shortcut_properties.set_arguments(arguments); |
| - shortcut_properties.set_description(description); |
| - shortcut_properties.set_icon(FilePath(icon_path), icon_index); |
| - shortcut_properties.set_app_id(app_id); |
| - shortcut_properties.set_dual_mode(is_dual_mode); |
| - return base::win::CreateOrUpdateShortcutLink( |
| - FilePath(shortcut), shortcut_properties, operation); |
| -} |
| - |
| bool ShellUtil::GetUserSpecificRegistrySuffix(string16* suffix) { |
| // Use a thread-safe cache for the user's suffix. |
| static base::LazyInstance<UserSpecificRegistrySuffix>::Leaky suffix_instance = |