Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(997)

Unified Diff: chrome/browser/profiles/profile_shortcut_manager_win.cc

Issue 1540543002: Uniquify profile shortcut name. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix review notes. Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/profiles/profile_shortcut_manager_win.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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(
« no previous file with comments | « chrome/browser/profiles/profile_shortcut_manager_win.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698