Index: chrome/browser/profiles/profile_shortcut_manager_win.cc |
diff --git a/chrome/browser/profiles/profile_shortcut_manager_win.cc b/chrome/browser/profiles/profile_shortcut_manager_win.cc |
index 0061b4cfcd4938ccc2b9094afd2968fd3708fe0a..422fcb0945029fac0dd28625bd5c8c957079d4ab 100644 |
--- a/chrome/browser/profiles/profile_shortcut_manager_win.cc |
+++ b/chrome/browser/profiles/profile_shortcut_manager_win.cc |
@@ -6,6 +6,8 @@ |
#include <shlobj.h> // For SHChangeNotify(). |
+#include <algorithm> |
+#include <set> |
#include <string> |
#include <vector> |
@@ -289,33 +291,56 @@ bool IsChromeShortcut(const base::FilePath& path, |
return ConvertToLongPath(target_path) == ConvertToLongPath(chrome_exe); |
} |
-// Populates |paths| with the file paths of Chrome desktop shortcuts that have |
+// Checks if |path| is the Chrome desktop shortcut (|chrome_exe|) that have |
// the specified |command_line|. If |include_empty_command_lines| is true, |
// Chrome desktop shortcuts with empty command lines will also be included. |
Alexei Svitkine (slow)
2016/01/15 18:01:38
This comment doesn't seem to be applicable to the
Michael K. (Yandex Team)
2016/01/18 09:37:54
I've looked through the code and it seems that com
|
-void ListDesktopShortcutsWithCommandLine(const base::FilePath& chrome_exe, |
- const base::string16& command_line, |
- bool include_empty_command_lines, |
- std::vector<base::FilePath>* paths) { |
- base::FilePath user_shortcuts_directory; |
- if (!GetDesktopShortcutsDirectories(&user_shortcuts_directory, NULL)) |
- return; |
- |
- base::FileEnumerator enumerator(user_shortcuts_directory, false, |
- base::FileEnumerator::FILES); |
- for (base::FilePath path = enumerator.Next(); !path.empty(); |
- path = enumerator.Next()) { |
+struct ChromeCommandLineFilter { |
+ const base::FilePath& chrome_exe; |
+ const base::string16& command_line; |
+ bool include_empty_command_lines; |
+ |
+ ChromeCommandLineFilter(const base::FilePath& chrome_exe, |
+ const base::string16& command_line, |
+ bool include_empty_command_lines) |
+ : chrome_exe(chrome_exe), |
+ command_line(command_line), |
+ include_empty_command_lines(include_empty_command_lines) {} |
+ |
+ bool operator()(const base::FilePath& path) const { |
base::string16 shortcut_command_line; |
if (!IsChromeShortcut(path, chrome_exe, &shortcut_command_line)) |
- continue; |
+ return false; |
// TODO(asvitkine): Change this to build a CommandLine object and ensure all |
// args from |command_line| are present in the shortcut's CommandLine. This |
// will be more robust when |command_line| contains multiple args. |
if ((shortcut_command_line.empty() && include_empty_command_lines) || |
(shortcut_command_line.find(command_line) != base::string16::npos)) { |
- paths->push_back(path); |
+ return true; |
} |
+ return false; |
} |
+}; |
+ |
+// Get the file paths of desktop files and folders optionally filtered |
+// by |filter|. |
+std::set<base::FilePath> ListUserDesktopContents( |
+ const ChromeCommandLineFilter* filter) { |
+ std::set<base::FilePath> res; |
Alexei Svitkine (slow)
2016/01/15 18:01:38
Nit: result
Michael K. (Yandex Team)
2016/01/18 09:37:54
Done.
|
+ |
+ base::FilePath user_shortcuts_directory; |
+ if (!GetDesktopShortcutsDirectories(&user_shortcuts_directory, nullptr)) |
+ return res; |
+ |
+ base::FileEnumerator enumerator( |
+ user_shortcuts_directory, false, |
+ base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); |
+ for (base::FilePath path = enumerator.Next(); !path.empty(); |
+ path = enumerator.Next()) { |
+ if (!filter || (*filter)(path)) |
+ res.insert(path); |
+ } |
+ return res; |
} |
// Renames the given desktop shortcut and informs the shell of this change. |
@@ -336,8 +361,12 @@ bool RenameDesktopShortcut(const base::FilePath& old_shortcut_path, |
// Renames an existing Chrome desktop profile shortcut. Must be called on the |
// FILE thread. |
Alexei Svitkine (slow)
2016/01/15 18:01:38
Document the new params.
Michael K. (Yandex Team)
2016/01/18 09:37:54
Done.
|
void RenameChromeDesktopShortcutForProfile( |
- const base::string16& old_shortcut_filename, |
- const base::string16& new_shortcut_filename) { |
+ const base::string16& old_profile_name, |
+ const base::string16& new_profile_name, |
+ std::set<base::FilePath>* profile_shortcuts, |
+ std::set<base::FilePath>* desktop_contents) { |
+ DCHECK(profile_shortcuts); |
+ DCHECK(desktop_contents); |
DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
base::FilePath user_shortcuts_directory; |
@@ -347,32 +376,70 @@ void RenameChromeDesktopShortcutForProfile( |
return; |
} |
- const base::FilePath old_shortcut_path = |
- user_shortcuts_directory.Append(old_shortcut_filename); |
+ BrowserDistribution* distribution = BrowserDistribution::GetDistribution(); |
+ |
+ // Get a new unique shortcut name. |
+ const base::string16 new_shortcut_filename = |
+ profiles::internal::GetUniqueShortcutFilenameForProfile( |
+ new_profile_name, distribution, *desktop_contents); |
const base::FilePath new_shortcut_path = |
user_shortcuts_directory.Append(new_shortcut_filename); |
- if (base::PathExists(old_shortcut_path)) { |
+ if (!profile_shortcuts->empty()) { |
+ // From all profile_shortcuts choose only with known (canonical) name. |
+ profiles::internal::ShortcutFilenameMatcher matcher(old_profile_name, |
+ distribution); |
+ auto it = std::find_if(profile_shortcuts->begin(), profile_shortcuts->end(), |
+ [&matcher](const base::FilePath& p) { |
+ return matcher.IsCanonical(p.BaseName().value()); |
+ }); |
+ // If all profile_shortcuts were renamed by user, respect it and do not |
+ // rename. |
+ if (it == profile_shortcuts->end()) |
+ return; |
+ const base::FilePath old_shortcut_path = *it; |
+ |
// Rename the old shortcut unless a system-level shortcut exists at the |
// destination, in which case the old shortcut is simply deleted. |
const base::FilePath possible_new_system_shortcut = |
system_shortcuts_directory.Append(new_shortcut_filename); |
- if (base::PathExists(possible_new_system_shortcut)) |
- base::DeleteFile(old_shortcut_path, false); |
- else if (!RenameDesktopShortcut(old_shortcut_path, new_shortcut_path)) |
- DLOG(ERROR) << "Could not rename Windows profile desktop shortcut."; |
+ if (base::PathExists(possible_new_system_shortcut)) { |
+ if (base::DeleteFile(old_shortcut_path, false)) { |
+ profile_shortcuts->erase(old_shortcut_path); |
+ desktop_contents->erase(old_shortcut_path); |
+ } else { |
+ DLOG(ERROR) << "Could not delete Windows profile desktop shortcut."; |
+ } |
+ } else { |
+ if (RenameDesktopShortcut(old_shortcut_path, new_shortcut_path)) { |
+ profile_shortcuts->erase(old_shortcut_path); |
+ desktop_contents->erase(old_shortcut_path); |
+ profile_shortcuts->insert(new_shortcut_path); |
+ desktop_contents->insert(new_shortcut_path); |
+ } else { |
+ DLOG(ERROR) << "Could not rename Windows profile desktop shortcut."; |
+ } |
+ } |
} else { |
- // If the shortcut does not exist, it may have been renamed by the user. In |
- // that case, its name should not be changed. |
+ // If the shortcut does not exist, it may have been deleted by the user. |
// It's also possible that a system-level shortcut exists instead - this |
// should only be the case for the original Chrome shortcut from an |
// installation. If that's the case, copy that one over - it will get its |
// properties updated by |
// |CreateOrUpdateDesktopShortcutsAndIconForProfile()|. |
+ const auto old_shortcut_filename = |
+ profiles::internal::GetShortcutFilenameForProfile(old_profile_name, |
+ distribution); |
const base::FilePath possible_old_system_shortcut = |
system_shortcuts_directory.Append(old_shortcut_filename); |
- if (base::PathExists(possible_old_system_shortcut)) |
- base::CopyFile(possible_old_system_shortcut, new_shortcut_path); |
+ if (base::PathExists(possible_old_system_shortcut)) { |
+ if (base::CopyFile(possible_old_system_shortcut, new_shortcut_path)) { |
+ profile_shortcuts->insert(new_shortcut_path); |
+ desktop_contents->insert(new_shortcut_path); |
+ } else { |
+ DLOG(ERROR) << "Could not copy Windows profile desktop shortcut."; |
+ } |
+ } |
} |
} |
@@ -428,24 +495,31 @@ void CreateOrUpdateDesktopShortcutsAndIconForProfile( |
// the following code may result in NOTREACHED() being hit. |
DCHECK(distribution->CanCreateDesktopShortcuts()); |
+ std::set<base::FilePath> desktop_contents = ListUserDesktopContents(nullptr); |
+ |
+ const base::string16 command_line = |
+ profiles::internal::CreateProfileShortcutFlags(params.profile_path); |
+ ChromeCommandLineFilter filter( |
+ chrome_exe, command_line, |
+ params.action == ProfileShortcutManagerWin::UPDATE_NON_PROFILE_SHORTCUTS); |
+ |
+ std::set<base::FilePath> shortcuts; |
+ // Do not call ListUserDesktopContents again (but with filter) to avoid |
+ // excess work inside it. Just reuse non-filtered desktop_contents. |
+ // We need both of them (desktop_contents and shortcuts) later. |
+ std::copy_if(desktop_contents.begin(), desktop_contents.end(), |
+ std::inserter(shortcuts, shortcuts.begin()), filter); |
+ |
if (params.old_profile_name != params.profile_name) { |
- const base::string16 old_shortcut_filename = |
- profiles::internal::GetShortcutFilenameForProfile( |
- params.old_profile_name, |
- distribution); |
- const base::string16 new_shortcut_filename = |
- profiles::internal::GetShortcutFilenameForProfile(params.profile_name, |
- distribution); |
- RenameChromeDesktopShortcutForProfile(old_shortcut_filename, |
- new_shortcut_filename); |
+ RenameChromeDesktopShortcutForProfile(params.old_profile_name, |
+ params.profile_name, &shortcuts, |
+ &desktop_contents); |
} |
ShellUtil::ShortcutProperties properties(ShellUtil::CURRENT_USER); |
installer::Product product(distribution); |
product.AddDefaultShortcutProperties(chrome_exe, &properties); |
- const base::string16 command_line = |
- profiles::internal::CreateProfileShortcutFlags(params.profile_path); |
// Only set the profile-specific properties when |profile_name| is non empty. |
// If it is empty, it means the shortcut being created should be a regular, |
@@ -465,22 +539,17 @@ void CreateOrUpdateDesktopShortcutsAndIconForProfile( |
ShellUtil::ShortcutOperation operation = |
ShellUtil::SHELL_SHORTCUT_REPLACE_EXISTING; |
- std::vector<base::FilePath> shortcuts; |
- ListDesktopShortcutsWithCommandLine(chrome_exe, command_line, |
- params.action == ProfileShortcutManagerWin::UPDATE_NON_PROFILE_SHORTCUTS, |
- &shortcuts); |
if (params.create_mode == ProfileShortcutManagerWin::CREATE_WHEN_NONE_FOUND && |
shortcuts.empty()) { |
const base::string16 shortcut_name = |
- profiles::internal::GetShortcutFilenameForProfile(params.profile_name, |
- distribution); |
- shortcuts.push_back(base::FilePath(shortcut_name)); |
+ profiles::internal::GetUniqueShortcutFilenameForProfile( |
+ params.profile_name, distribution, desktop_contents); |
+ shortcuts.insert(base::FilePath(shortcut_name)); |
operation = ShellUtil::SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL; |
} |
- for (size_t i = 0; i < shortcuts.size(); ++i) { |
- const base::FilePath shortcut_name = |
- shortcuts[i].BaseName().RemoveExtension(); |
+ for (const auto& shortcut : shortcuts) { |
+ const base::FilePath shortcut_name = shortcut.BaseName().RemoveExtension(); |
properties.set_shortcut_name(shortcut_name.value()); |
ShellUtil::CreateOrUpdateShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP, |
distribution, properties, operation); |
@@ -521,20 +590,18 @@ void DeleteDesktopShortcuts(const base::FilePath& profile_path, |
const base::string16 command_line = |
profiles::internal::CreateProfileShortcutFlags(profile_path); |
- std::vector<base::FilePath> shortcuts; |
- ListDesktopShortcutsWithCommandLine(chrome_exe, command_line, false, |
- &shortcuts); |
+ ChromeCommandLineFilter filter(chrome_exe, command_line, false); |
+ const std::set<base::FilePath> shortcuts = ListUserDesktopContents(&filter); |
- for (size_t i = 0; i < shortcuts.size(); ++i) { |
+ for (const auto& shortcut : shortcuts) { |
// Use base::DeleteFile() instead of ShellUtil::RemoveShortcuts(), as the |
// latter causes non-profile taskbar shortcuts to be removed since it |
// doesn't consider the command-line of the shortcuts it deletes. |
// TODO(huangs): Refactor with ShellUtil::RemoveShortcuts(). |
- base::win::UnpinShortcutFromTaskbar(shortcuts[i]); |
- base::DeleteFile(shortcuts[i], false); |
+ base::win::UnpinShortcutFromTaskbar(shortcut); |
+ base::DeleteFile(shortcut, false); |
// Notify the shell that the shortcut was deleted to ensure desktop refresh. |
- SHChangeNotify(SHCNE_DELETE, SHCNF_PATH, shortcuts[i].value().c_str(), |
- NULL); |
+ SHChangeNotify(SHCNE_DELETE, SHCNF_PATH, shortcut.value().c_str(), nullptr); |
} |
// If |ensure_shortcuts_remain| is true and deleting this profile caused the |
@@ -572,9 +639,8 @@ bool HasAnyProfileShortcuts(const base::FilePath& profile_path) { |
const base::string16 command_line = |
profiles::internal::CreateProfileShortcutFlags(profile_path); |
- std::vector<base::FilePath> shortcuts; |
- ListDesktopShortcutsWithCommandLine(chrome_exe, command_line, false, |
- &shortcuts); |
+ ChromeCommandLineFilter filter(chrome_exe, command_line, false); |
+ const std::set<base::FilePath> shortcuts = ListUserDesktopContents(&filter); |
return !shortcuts.empty(); |
} |
@@ -643,6 +709,59 @@ base::string16 GetShortcutFilenameForProfile( |
return shortcut_name + installer::kLnkExt; |
} |
+base::string16 GetUniqueShortcutFilenameForProfile( |
+ const base::string16& profile_name, |
+ BrowserDistribution* distribution, |
+ const std::set<base::FilePath>& excludes) { |
+ std::set<base::string16> excludes_names; |
+ std::transform(excludes.begin(), excludes.end(), |
+ std::inserter(excludes_names, excludes_names.begin()), |
+ [](const base::FilePath& e) { return e.BaseName().value(); }); |
+ |
+ const auto base_name = |
+ GetShortcutFilenameForProfile(profile_name, distribution); |
+ auto name = base_name; |
+ for (int uniquifier = 1; excludes_names.count(name) > 0; ++uniquifier) |
Alexei Svitkine (slow)
2016/01/15 18:01:38
Nit: {}'s
Michael K. (Yandex Team)
2016/01/18 09:37:54
Done.
|
+ name = base::FilePath(base_name) |
Alexei Svitkine (slow)
2016/01/15 18:01:38
Move the FilePath outside the loop.
Michael K. (Yandex Team)
2016/01/18 09:37:54
Acknowledged.
|
+ .InsertBeforeExtensionASCII(base::StringPrintf(" (%d)", uniquifier)) |
+ .value(); |
+ return name; |
+} |
+ |
+// Corresponds to GetUniqueShortcutFilenameForProfile. |
+ShortcutFilenameMatcher::ShortcutFilenameMatcher( |
+ const base::string16& profile_name, |
+ BrowserDistribution* distribution) |
+ : profile_shortcut_filename_( |
+ GetShortcutFilenameForProfile(profile_name, distribution)), |
+ lnk_ext_(installer::kLnkExt), |
+ profile_shortcut_name_(profile_shortcut_filename_) { |
+ DCHECK(profile_shortcut_name_.ends_with(lnk_ext_)); |
+ profile_shortcut_name_.remove_suffix(lnk_ext_.size()); |
+} |
+ |
+bool ShortcutFilenameMatcher::IsCanonical( |
+ const base::string16& filename) const { |
+ if (filename == profile_shortcut_filename_) |
+ return true; |
+ |
+ base::StringPiece16 shortcut_suffix(filename); |
+ if (!shortcut_suffix.starts_with(profile_shortcut_name_)) |
+ return false; |
+ shortcut_suffix.remove_prefix(profile_shortcut_name_.size()); |
+ |
+ if (!shortcut_suffix.ends_with(lnk_ext_)) |
+ return false; |
+ shortcut_suffix.remove_suffix(lnk_ext_.size()); |
+ |
+ if (shortcut_suffix.size() < 4 || !shortcut_suffix.starts_with(L" (") || |
+ !shortcut_suffix.ends_with(L")")) { |
+ return false; |
+ } |
+ return std::all_of(shortcut_suffix.begin() + 2, shortcut_suffix.end() - 1, |
+ iswdigit); |
+} |
+ |
base::string16 CreateProfileShortcutFlags(const base::FilePath& profile_path) { |
return base::StringPrintf(L"--%ls=\"%ls\"", |
base::ASCIIToUTF16( |