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 4a60171174d5526e3b21bab49799e9406b19f4c5..c09f7b6970b05c1c70e74ac106fbf3aa694fbb34 100644 |
| --- a/chrome/installer/util/shell_util.cc |
| +++ b/chrome/installer/util/shell_util.cc |
| @@ -1159,32 +1159,212 @@ ShellUtil::DefaultState ProbeProtocolHandlers( |
| return ProbeOpenCommandHandlers(protocols, num_protocols); |
| } |
| -// Removes shortcut at |shortcut_path| if it is a shortcut that points to |
| -// |target_exe|. If |delete_folder| is true, deletes the parent folder of |
| -// the shortcut completely. Returns true if either the shortcut was deleted |
| -// successfully or if the shortcut did not point to |target_exe|. |
| -bool MaybeRemoveShortcutAtPath(const base::FilePath& shortcut_path, |
| - const base::FilePath& target_exe, |
| - bool delete_folder) { |
| - base::FilePath target_path; |
| - if (!base::win::ResolveShortcut(shortcut_path, &target_path, NULL)) |
| +// (Windows 8+) Returns the folder for app shortcuts, or empty if none found. |
| +base::FilePath GetAppShortcutsFolder(BrowserDistribution* dist, |
| + const string16& target_exe) { |
| + if (base::win::GetVersion() < base::win::VERSION_WIN8) |
| + return base::FilePath(); |
| + |
| + base::FilePath folder; |
| + if (!PathService::Get(base::DIR_APP_SHORTCUTS, &folder)) { |
| + LOG(ERROR) << "Could not get application shortcuts location."; |
| + return base::FilePath(); |
| + } |
| + |
| + folder = folder.Append( |
| + ShellUtil::GetBrowserModelId(dist, |
|
gab
2013/04/24 21:41:46
nit: wrap |dist| on the line below.
The rule is e
huangs
2013/04/25 16:27:46
Done.
|
| + InstallUtil::IsPerUserInstall(target_exe.c_str()))); |
| + if (!file_util::DirectoryExists(folder)) { |
| + VLOG(1) << "No start screen shortcuts."; |
| + return base::FilePath(); |
| + } |
| + |
| + return folder; |
| +} |
| + |
| +const wchar_t kAllFiles[] = L"*"; |
| + |
| +// Helper to perform common operations to shortcuts in a folder. |
| +// Implement the ActOnShortcut() and ActOnFolder() to define operations. |
| +class BatchShortcutOperation { |
|
gab
2013/04/24 21:41:46
I think it would be nicer, less verbose, and easie
huangs
2013/04/25 16:27:46
I think it's important to group it into a class, b
|
| + public: |
| + BatchShortcutOperation() {} |
| + virtual ~BatchShortcutOperation() {} |
| + |
| + // Goes through |shortcut_folder| and seeks shortcuts with names that match |
| + // |name_filter| (empty = none), and with targets given by |target_exe|. |
| + // Calls ActOnShortcut() with each matching shortcuts. Afterwards, calls |
| + // ActOnFolder() with |shortcut_folder|. |
| + // Returns true iff all operations are successful. |
| + bool Run(const base::FilePath& shortcut_folder, |
| + const string16& name_filter, |
| + const base::FilePath& target_exe); |
| + |
| + // Wrapper for Run(): first determine shortcut path based on given data. |
| + // |name| can specify the name for a single shortcut (without ".lnk" suffix), |
|
gab
2013/04/24 21:41:46
s/suffix/extension
gab
2013/04/24 21:41:46
I don't like relying on the extension being presen
huangs
2013/04/25 16:27:46
Done.
huangs
2013/04/25 16:27:46
Done; adding ".lnk" if missing (exception is empty
|
| + // or all shortcuts if NULL. |
| + bool RunForLocation(ShellUtil::ShortcutLocation location, |
| + BrowserDistribution* dist, |
| + const base::FilePath& target_exe, |
| + ShellUtil::ShellChange level, |
| + const string16* name); |
| + |
| + // Wrapper for Run(): specifying all shortcuts in the taskbar. |
| + bool RunForTaskbar(const base::FilePath& target_exe); |
| + |
| + // Returns: true on success. |
| + virtual bool ActOnShortcut(const base::FilePath& shortcut_path) const = 0; |
| + |
| + // Returns: true on success. |
| + virtual bool ActOnFolder(const base::FilePath& shortcut_folder) const = 0; |
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(BatchShortcutOperation); |
| +}; |
| + |
| +// |name_filter| will have ".lnk" appended. |
| +bool BatchShortcutOperation::Run(const base::FilePath& shortcut_folder, |
| + const string16& name_filter, |
| + const base::FilePath& target_exe) { |
| + InstallUtil::ProgramCompare target_compare(target_exe); |
| + bool success = true; |
| + if (!name_filter.empty()) { |
| + string16 pattern = name_filter + installer::kLnkExt; |
| + file_util::FileEnumerator enumerator(shortcut_folder, false, |
| + file_util::FileEnumerator::FILES, pattern); |
| + for (base::FilePath shortcut_path = enumerator.Next(); |
| + !shortcut_path.empty(); |
| + shortcut_path = enumerator.Next()) { |
| + if (shortcut_path.Extension() == installer::kLnkExt) { |
|
gab
2013/04/24 21:41:46
You are already only enumerating files with the .l
huangs
2013/04/25 16:27:46
Changed to DCHECK_EQ().
|
| + base::FilePath target_path; |
| + if (base::win::ResolveShortcut(shortcut_path, &target_path, NULL)) { |
| + if (target_compare.EvaluatePath(target_path)) |
| + success = ActOnShortcut(shortcut_path) && success; |
| + } else { |
| + LOG(ERROR) << "Cannot resolve shortcut at " << shortcut_path.value(); |
| + success = false; |
| + } |
| + } |
| + } |
| + } |
| + success = ActOnFolder(shortcut_folder) && success; |
| + return success; |
| +} |
| + |
| +bool BatchShortcutOperation::RunForLocation( |
| + ShellUtil::ShortcutLocation location, |
| + BrowserDistribution* dist, |
| + const base::FilePath& target_exe, |
| + ShellUtil::ShellChange level, |
| + const string16* name) { |
|
gab
2013/04/24 21:41:46
If |name| is changed to a pattern (as suggested in
huangs
2013/04/25 16:27:46
I think I'll get rid of the ActOnFolder() stuff; w
|
| + base::FilePath shortcut_folder; |
| + if (!ShellUtil::GetShortcutPath(location, dist, level, &shortcut_folder) || |
| + shortcut_folder.empty()) { |
| + NOTREACHED(); |
| return false; |
| + } |
| - if (InstallUtil::ProgramCompare(target_exe).EvaluatePath(target_path)) { |
| - // Unpin the shortcut if it was ever pinned by the user or the installer. |
| + return Run(shortcut_folder, name ? *name : kAllFiles, target_exe); |
| +} |
| + |
| +bool BatchShortcutOperation::RunForTaskbar(const base::FilePath& target_exe) { |
| + base::FilePath taskbar_pins_path; |
| + if (!PathService::Get(base::DIR_TASKBAR_PINS, &taskbar_pins_path) || |
|
gab
2013/04/24 21:41:46
Feels like we should augment GetShortcutPath() ins
huangs
2013/04/25 16:27:46
Might be solved by removing ActOnFolder().
|
| + !file_util::PathExists(taskbar_pins_path)) { |
| + LOG(ERROR) << "Couldn't find path to taskbar pins."; |
| + return false; |
| + } |
| + |
| + return Run(taskbar_pins_path, kAllFiles, target_exe); |
|
gab
2013/04/24 21:41:46
Taskbar pins are special though, they have to be e
huangs
2013/04/25 16:27:46
Per earlier discussion, having a dedicated unpin o
|
| +} |
| + |
| +// Batch shortcut operation to unpin and/or delete shortcuts in a folder, |
| +// and possibly remove the folder itself. |
| +class BatchShortcutDelete: public BatchShortcutOperation { |
| + public: |
| + BatchShortcutDelete(bool unpin, bool delete_file, bool delete_folder); |
| + virtual ~BatchShortcutDelete() {} |
| + |
| + // Unpins and/or removes |shortcut_path|. |
| + // Returns true if deletion successful or if no deletion occurs. |
| + virtual bool ActOnShortcut(const base::FilePath& shortcut_path) const |
| + OVERRIDE; |
| + |
| + // Removes |shortcut_folder| if |delete_folder_|. |
| + // Returns true if deletion successful. |
| + virtual bool ActOnFolder(const base::FilePath& shortcut_folder) const |
| + OVERRIDE; |
| + |
| + private: |
| + bool unpin_; |
| + bool delete_file_; |
| + bool delete_folder_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(BatchShortcutDelete); |
| +}; |
| + |
| +BatchShortcutDelete::BatchShortcutDelete(bool unpin, |
| + bool delete_file, |
| + bool delete_folder) |
|
gab
2013/04/24 21:41:46
(bool, bool, bool, bool, bool, bool, bool) interfa
huangs
2013/04/25 16:27:46
Adding accessors to explicitly set flags. But thi
|
| + : unpin_(unpin), |
| + delete_file_(delete_file), |
| + delete_folder_(delete_folder) { |
| +} |
| + |
| +bool BatchShortcutDelete::ActOnShortcut(const base::FilePath& shortcut_path) |
| + const { |
| + if (unpin_) { |
| VLOG(1) << "Trying to unpin " << shortcut_path.value(); |
| if (!base::win::TaskbarUnpinShortcutLink(shortcut_path.value().c_str())) { |
|
gab
2013/04/24 21:41:46
Did you check who uses RemoveShortcut(), is it sti
huangs
2013/04/25 16:27:46
There a routine that avoids calling this BECAUSE o
|
| VLOG(1) << shortcut_path.value() |
| << " wasn't pinned (or the unpin failed)."; |
| + // Not an error condition, since shortcut might not be pinned. |
| } |
| - if (delete_folder) |
| - return file_util::Delete(shortcut_path.DirName(), true); |
| - else |
| - return file_util::Delete(shortcut_path, false); |
| } |
| + return delete_file_ ? file_util::Delete(shortcut_path, false) : true; |
| +} |
| + |
| +bool BatchShortcutDelete::ActOnFolder(const base::FilePath& shortcut_folder) |
| + const { |
| + return delete_folder_ ? file_util::Delete(shortcut_folder, true) : true; |
| +} |
| + |
| +// Batch shortcut operation to set target. |
| +class BatchShortcutSetTarget: public BatchShortcutOperation { |
| + public: |
| + explicit BatchShortcutSetTarget(const base::FilePath& target); |
| + virtual ~BatchShortcutSetTarget() {} |
| + |
| + // Sets the target of |shortcut_path| to |target_|. |
| + // Returns true on success. |
| + virtual bool ActOnShortcut(const base::FilePath& shortcut_path) const |
| + OVERRIDE; |
| + |
| + // No-op; returns true. |
| + virtual bool ActOnFolder(const base::FilePath& shortcut_path) const OVERRIDE; |
| + |
| + private: |
| + base::FilePath target_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(BatchShortcutSetTarget); |
| +}; |
| + |
| +BatchShortcutSetTarget::BatchShortcutSetTarget( |
| + const base::FilePath& target) |
| + : target_(target) { |
| +} |
| + |
| +bool BatchShortcutSetTarget::ActOnShortcut(const base::FilePath& shortcut_path) |
| + const { |
| + base::win::ShortcutProperties shortcut_properties; |
| + shortcut_properties.set_target(target_); |
| + shortcut_properties.set_working_dir(target_.DirName()); |
| + return base::win::CreateOrUpdateShortcutLink( |
| + shortcut_path, shortcut_properties, base::win::SHORTCUT_UPDATE_EXISTING); |
| +} |
| - // The shortcut at |shortcut_path| doesn't point to |target_exe|, act as if |
| - // our shortcut had been deleted. |
| +bool BatchShortcutSetTarget::ActOnFolder(const base::FilePath& shortcut_path) |
| + const { |
| return true; |
| } |
| @@ -1858,103 +2038,85 @@ bool ShellUtil::RegisterChromeForProtocol(BrowserDistribution* dist, |
| } |
| } |
| +// static |
| bool ShellUtil::RemoveShortcut(ShellUtil::ShortcutLocation location, |
| BrowserDistribution* dist, |
| const base::FilePath& target_exe, |
| ShellChange level, |
| const string16* shortcut_name) { |
| - const bool delete_folder = (location == SHORTCUT_LOCATION_START_MENU); |
| - |
| - base::FilePath shortcut_folder; |
| - if (!GetShortcutPath(location, dist, level, &shortcut_folder) || |
| - shortcut_folder.empty()) { |
| - NOTREACHED(); |
| - return false; |
| - } |
| - |
| - if (!delete_folder && !shortcut_name) { |
| - file_util::FileEnumerator enumerator(shortcut_folder, false, |
| - file_util::FileEnumerator::FILES); |
| - bool had_failures = false; |
| - for (base::FilePath path = enumerator.Next(); !path.empty(); |
| - path = enumerator.Next()) { |
| - if (path.Extension() != installer::kLnkExt) |
| - continue; |
| - |
| - if (!MaybeRemoveShortcutAtPath(path, target_exe, delete_folder)) |
| - had_failures = true; |
| - } |
| - return !had_failures; |
| - } |
| - |
| - const string16 shortcut_base_name( |
| - (shortcut_name ? *shortcut_name : dist->GetAppShortCutName()) + |
| - installer::kLnkExt); |
| - const base::FilePath shortcut_path( |
| - shortcut_folder.Append(shortcut_base_name)); |
| - if (!file_util::PathExists(shortcut_path)) |
| - return true; |
| - |
| - return MaybeRemoveShortcutAtPath(shortcut_path, target_exe, delete_folder); |
| + bool delete_folder = (location == SHORTCUT_LOCATION_START_MENU); |
| + BatchShortcutDelete op(true, true, delete_folder); // Unpon and delete-file. |
|
gab
2013/04/24 21:41:46
nit: Unpon :)
huangs
2013/04/25 16:27:46
Done. :)
|
| + return op.RunForLocation(location, dist, target_exe, level, shortcut_name); |
| } |
| +// static |
| void ShellUtil::RemoveTaskbarShortcuts(const string16& target_exe) { |
| if (base::win::GetVersion() < base::win::VERSION_WIN7) |
| return; |
| - base::FilePath taskbar_pins_path; |
| - if (!PathService::Get(base::DIR_TASKBAR_PINS, &taskbar_pins_path) || |
| - !file_util::PathExists(taskbar_pins_path)) { |
| - LOG(ERROR) << "Couldn't find path to taskbar pins."; |
| - return; |
| - } |
| - |
| - file_util::FileEnumerator shortcuts_enum( |
| - taskbar_pins_path, false, |
| - file_util::FileEnumerator::FILES, FILE_PATH_LITERAL("*.lnk")); |
| - |
| - base::FilePath target_path(target_exe); |
| - InstallUtil::ProgramCompare target_compare(target_path); |
| - for (base::FilePath shortcut_path = shortcuts_enum.Next(); |
| - !shortcut_path.empty(); |
| - shortcut_path = shortcuts_enum.Next()) { |
| - base::FilePath read_target; |
| - if (!base::win::ResolveShortcut(shortcut_path, &read_target, NULL)) { |
| - LOG(ERROR) << "Couldn't resolve shortcut at " << shortcut_path.value(); |
| - continue; |
| - } |
| - if (target_compare.EvaluatePath(read_target)) { |
| - // Unpin this shortcut if it points to |target_exe|. |
| - base::win::TaskbarUnpinShortcutLink(shortcut_path.value().c_str()); |
| - } |
| - } |
| + BatchShortcutDelete op(true, false, false); // Unpin only. |
| + op.RunForTaskbar(base::FilePath(target_exe)); |
| } |
| +// static |
| void ShellUtil::RemoveStartScreenShortcuts(BrowserDistribution* dist, |
| const string16& target_exe) { |
| if (base::win::GetVersion() < base::win::VERSION_WIN8) |
| return; |
| - base::FilePath app_shortcuts_path; |
| - if (!PathService::Get(base::DIR_APP_SHORTCUTS, &app_shortcuts_path)) { |
| - LOG(ERROR) << "Could not get application shortcuts location to delete" |
| - << " start screen shortcuts."; |
| + base::FilePath folder(GetAppShortcutsFolder(dist, target_exe)); |
|
gab
2013/04/24 21:41:46
This could probably also go in GetShortcutPath() a
huangs
2013/04/25 16:27:46
Will do this next. Can I remove / redo the messag
|
| + if (folder.empty()) |
| return; |
| + |
| + BatchShortcutDelete op(false, false, true); // Delete-folder only. |
| + VLOG(1) << "Removing start screen shortcuts from " << folder.value(); |
| + if (!op.Run(folder, L"", base::FilePath())) { |
| + LOG(ERROR) << "Failed to remove start screen shortcuts from " |
| + << folder.value(); |
| } |
| +} |
| + |
| +// static |
| +bool ShellUtil::MigrateShortcut(ShellUtil::ShortcutLocation location, |
| + BrowserDistribution* dist, |
| + const base::FilePath& old_target_exe, |
| + const base::FilePath& new_target_exe, |
| + ShellChange level, |
| + const string16* shortcut_name) { |
| + bool delete_folder = (location == SHORTCUT_LOCATION_START_MENU); |
| + BatchShortcutSetTarget op(new_target_exe); |
| + return op.RunForLocation(location, dist, old_target_exe, level, |
| + shortcut_name); |
| +} |
| - app_shortcuts_path = app_shortcuts_path.Append( |
| - GetBrowserModelId(dist, |
| - InstallUtil::IsPerUserInstall(target_exe.c_str()))); |
| - if (!file_util::DirectoryExists(app_shortcuts_path)) { |
| - VLOG(1) << "No start screen shortcuts to delete."; |
| +// static |
| +void ShellUtil::MigrateTaskbarShortcuts(const string16& old_target_exe, |
| + const string16& new_target_exe) { |
| + if (base::win::GetVersion() < base::win::VERSION_WIN7) |
| return; |
| - } |
| - VLOG(1) << "Removing start screen shortcuts from " |
| - << app_shortcuts_path.value(); |
| - if (!file_util::Delete(app_shortcuts_path, true)) { |
| - LOG(ERROR) << "Failed to remove start screen shortcuts from " |
| - << app_shortcuts_path.value(); |
| + base::FilePath new_target_path(new_target_exe); |
| + BatchShortcutSetTarget op(new_target_path); |
| + op.RunForTaskbar(base::FilePath(old_target_exe)); |
| +} |
| + |
| +// static |
| +void ShellUtil::MigrateStartScreenShortcuts(BrowserDistribution* dist, |
| + const string16& old_target_exe, |
| + const string16& new_target_exe) { |
| + if (base::win::GetVersion() < base::win::VERSION_WIN8) |
| + return; |
| + |
| + base::FilePath folder(GetAppShortcutsFolder(dist, old_target_exe)); |
| + if (folder.empty()) |
| + return; |
| + |
| + base::FilePath new_target_path(new_target_exe); |
| + BatchShortcutSetTarget op(new_target_path); |
| + VLOG(1) << "Migrating start screen shortcuts from " << folder.value(); |
| + if (!op.Run(folder, kAllFiles, base::FilePath(old_target_exe))) { |
| + LOG(ERROR) << "Failed to migrate start screen shortcuts from " |
| + << folder.value(); |
| } |
| } |