Commit a60d187c authored by huangs@chromium.org's avatar huangs@chromium.org

On user-level Chrome self-destruct, make user-created shortcuts target system-level chrome.exe.

The old behavior was to delete all the shortcuts, which is rather user-hostile. With this change, the user-created shortcuts will be made to target the new system-level Chrome, to form a better transition experience.

- To determine whether or not a shortcut (to the user-level chrome.exe) is user-created, the heuristic we use is to test that the arguments of the shortcut are empty.
- The default shortcut to user-level chrome.exe is NOT retargeted, so it would get deleted (thus avoiding duplicate shortcut to the system-level Chrome).

Right now the "Chrome Apps" is not handled. This is a similar issue as http://crbug.com/238895 , and should be handled in a different CL.

BUG=235857

Review URL: https://chromiumcodereview.appspot.com/22382007

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@221343 0039d316-1c4b-4281-b951-d872f2087c98
parent b0f724cb
......@@ -328,6 +328,56 @@ void CloseChromeFrameHelperProcess() {
}
}
// Updates shortcuts to |old_target_exe| to target |new_target_exe| instead. If
// |require_args| is set, then only updates shortcuts with non-empty targets.
// This should only be called from user-level.
void RetargetShortcuts(const InstallerState& installer_state,
const Product& product,
const base::FilePath& old_target_exe,
const base::FilePath& new_target_exe,
bool require_args) {
BrowserDistribution* dist = product.distribution();
DCHECK(!installer_state.system_install());
ShellUtil::ShellChange install_level = ShellUtil::CURRENT_USER;
ShellUtil::ShortcutProperties updated_properties(install_level);
updated_properties.set_target(new_target_exe);
VLOG(1) << "Retargeting Desktop shortcuts.";
if (!ShellUtil::UpdateShortcuts(ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist,
install_level, old_target_exe, require_args,
updated_properties)) {
LOG(WARNING) << "Failed to retarget Desktop shortcuts.";
}
VLOG(1) << "Retargeting Quick Launch shortcuts.";
if (!ShellUtil::UpdateShortcuts(ShellUtil::SHORTCUT_LOCATION_QUICK_LAUNCH,
dist, install_level, old_target_exe,
require_args, updated_properties)) {
LOG(WARNING) << "Failed to retarget Quick Launch shortcuts.";
}
VLOG(1) << "Retargeting Start Menu shortcuts.";
if (!ShellUtil::UpdateShortcuts(ShellUtil::SHORTCUT_LOCATION_START_MENU, dist,
install_level, old_target_exe, require_args,
updated_properties)) {
LOG(WARNING) << "Failed to retarget Start Menu shortcuts.";
}
// Retarget pinned-to-taskbar shortcuts that point to |chrome_exe|.
if (!ShellUtil::UpdateShortcuts(ShellUtil::SHORTCUT_LOCATION_TASKBAR_PINS,
dist, ShellUtil::CURRENT_USER, old_target_exe,
require_args, updated_properties)) {
LOG(WARNING) << "Failed to retarget taskbar shortcuts at user-level.";
}
// Retarget the folder of secondary tiles from the start screen for |dist|.
if (!ShellUtil::UpdateShortcuts(ShellUtil::SHORTCUT_LOCATION_APP_SHORTCUTS,
dist, install_level, old_target_exe,
require_args, updated_properties)) {
LOG(WARNING) << "Failed to retarget start-screen shortcuts.";
}
}
// Deletes shortcuts at |install_level| from Start menu, Desktop,
// Quick Launch, taskbar, and secondary tiles on the Start Screen (Win8+).
// Only shortcuts pointing to |target_exe| will be removed.
......@@ -1136,6 +1186,26 @@ InstallStatus UninstallProduct(const InstallationState& original_state,
auto_launch_util::DisableAllAutoStartFeatures(
ASCIIToUTF16(chrome::kInitialProfile));
// Self-destruct flow: removing user-level Chrome because system-level
// Chrome exists.
if (cmd_line.HasSwitch(installer::switches::kSelfDestruct) &&
!installer_state.system_install()) {
const base::FilePath system_chrome_path(
GetChromeInstallPath(true, browser_dist).
Append(installer::kChromeExe));
VLOG(1) << "Retargeting user-generated Chrome shortcuts.";
if (base::PathExists(system_chrome_path)) {
// Retarget all user-generated shortcuts to user-level chrome.exe to
// system-level chrome.exe. Heuristic: consider only shortcuts that have
// non-empty args. Therefore the main user-level chrome.exe will not get
// retarged, and will get deleted by DeleteShortcuts() below.
RetargetShortcuts(installer_state, product, base::FilePath(chrome_exe),
system_chrome_path, true);
} else {
VLOG(1) << "Retarget failed: system-level Chrome not found.";
}
}
DeleteShortcuts(installer_state, product, base::FilePath(chrome_exe));
} else if (product.is_chrome_app_host()) {
......
......@@ -1203,13 +1203,15 @@ typedef base::Callback<bool(const base::FilePath& /*shortcut_path*/,
ShortcutFilterCallback;
// FilterTargetEq is a shortcut filter that matches only shortcuts that have a
// specific target.
// specific target, and optionally matches shortcuts that have non-empty
// arguments.
class FilterTargetEq {
public:
explicit FilterTargetEq(const base::FilePath& desired_target_exe);
FilterTargetEq(const base::FilePath& desired_target_exe, bool require_args);
// Returns true if filter rules are satisfied, i.e.:
// - |target_path| matches |desired_target_compare_|.
// - |target_path|'s target == |desired_target_compare_|, and
// - |args| is non-empty (if |require_args_| == true).
bool Match(const base::FilePath& target_path, const string16& args) const;
// A convenience routine to create a callback to call Match().
......@@ -1219,14 +1221,22 @@ class FilterTargetEq {
private:
InstallUtil::ProgramCompare desired_target_compare_;
bool require_args_;
};
FilterTargetEq::FilterTargetEq(const base::FilePath& desired_target_exe)
: desired_target_compare_(desired_target_exe) {}
FilterTargetEq::FilterTargetEq(const base::FilePath& desired_target_exe,
bool require_args)
: desired_target_compare_(desired_target_exe),
require_args_(require_args) {}
bool FilterTargetEq::Match(const base::FilePath& target_path,
const string16& args) const {
return desired_target_compare_.EvaluatePath(target_path);
if (!desired_target_compare_.EvaluatePath(target_path))
return false;
if (require_args_ && args.empty())
return false;
return true;
}
ShortcutFilterCallback FilterTargetEq::AsShortcutFilterCallback() {
......@@ -2042,7 +2052,7 @@ bool ShellUtil::RemoveShortcuts(ShellUtil::ShortcutLocation location,
if (!ShellUtil::ShortcutLocationIsSupported(location))
return true; // Vacuous success.
FilterTargetEq shortcut_filter(target_exe);
FilterTargetEq shortcut_filter(target_exe, false);
// Main operation to apply to each shortcut in the directory specified.
ShortcutOperationCallback shortcut_operation(
location == SHORTCUT_LOCATION_TASKBAR_PINS ?
......@@ -2064,11 +2074,12 @@ bool ShellUtil::UpdateShortcuts(
BrowserDistribution* dist,
ShellChange level,
const base::FilePath& target_exe,
bool require_args,
const ShellUtil::ShortcutProperties& properties) {
if (!ShellUtil::ShortcutLocationIsSupported(location))
return true; // Vacuous success.
FilterTargetEq shortcut_filter(target_exe);
FilterTargetEq shortcut_filter(target_exe, require_args);
ShortcutOperationCallback shortcut_operation(
base::Bind(&ShortcutOpUpdate, TranslateShortcutProperties(properties)));
return BatchShortcutAction(shortcut_filter.AsShortcutFilterCallback(),
......
......@@ -525,15 +525,18 @@ class ShellUtil {
ShellChange level,
const base::FilePath& target_exe);
// Applies the updates in |shortcut_properties| to all shortcuts in |location|
// that target |target_exe|.
// Returns true if all shortcuts pointing to |target_exe| are successfully
// updated, including the case where no such shortcuts are found.
// Applies the updates in |shortcut_properties| to all matching shortcuts
// in |location|, i.e.:
// - the shortcut's original target is |target_exe|,
// - if |require_args| is set, the original arguments are non-empty.
// Returns true if all updates to matching shortcuts are successful, including
// the vacuous case where no matching shortcuts are found.
static bool UpdateShortcuts(
ShellUtil::ShortcutLocation location,
BrowserDistribution* dist,
ShellChange level,
const base::FilePath& target_exe,
bool require_args,
const ShellUtil::ShortcutProperties& properties);
// Sets |suffix| to the base 32 encoding of the md5 hash of this user's sid
......
......@@ -451,7 +451,7 @@ TEST_F(ShellUtilShortcutTest, UpdateChromeShortcut) {
ASSERT_TRUE(ShellUtil::UpdateShortcuts(
ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_, ShellUtil::CURRENT_USER,
chrome_exe_, updated_properties));
chrome_exe_, false, updated_properties));
ShellUtil::ShortcutProperties expected_properties(test_properties_);
expected_properties.set_target(new_exe);
......@@ -478,7 +478,7 @@ TEST_F(ShellUtilShortcutTest, UpdateSystemLevelChromeShortcut) {
ASSERT_TRUE(ShellUtil::UpdateShortcuts(
ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_, ShellUtil::SYSTEM_LEVEL,
chrome_exe_, updated_properties));
chrome_exe_, false, updated_properties));
ShellUtil::ShortcutProperties expected_properties(test_properties_);
expected_properties.set_target(new_exe);
......@@ -490,18 +490,18 @@ TEST_F(ShellUtilShortcutTest, UpdateMultipleChromeShortcuts) {
const wchar_t kShortcutName1[] = L"Chrome 1";
const wchar_t kShortcutName2[] = L"Chrome 2";
// Setup shortcut 1.
// Setup shortcut 1, which has empty arguments.
test_properties_.set_shortcut_name(kShortcutName1);
test_properties_.set_arguments(L"");
ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_, test_properties_,
ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
string16 shortcut1_name(
string16(kShortcutName1).append(installer::kLnkExt));
string16 shortcut1_name(string16(kShortcutName1).append(installer::kLnkExt));
base::FilePath shortcut1_path(
fake_user_desktop_.path().Append(shortcut1_name));
ShellUtil::ShortcutProperties expected_properties1(test_properties_);
// Setup shortcut 2, which also has arguments.
// Setup shortcut 2, which has non-empty arguments.
string16 shortcut2_args = L"--profile-directory=\"Profile 2\"";
test_properties_.set_shortcut_name(kShortcutName2);
test_properties_.set_arguments(shortcut2_args);
......@@ -518,17 +518,31 @@ TEST_F(ShellUtilShortcutTest, UpdateMultipleChromeShortcuts) {
base::FilePath new_exe = temp_dir_.path().Append(kManganeseExe);
ShellUtil::ShortcutProperties updated_properties(ShellUtil::CURRENT_USER);
updated_properties.set_target(new_exe);
// |require_args| = true, so only changing shrotcuts that have non-empty
// arguments; only shortcut 2 is updated.
ASSERT_TRUE(ShellUtil::UpdateShortcuts(
ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_, ShellUtil::CURRENT_USER,
chrome_exe_, updated_properties));
chrome_exe_, /*require_args*/ true, updated_properties));
// Verify shortcut 1.
// |expected_properties1| was unchanged and still targets "chrome.exe", since
// it has empty target, yet we passed |require_args| = true.
ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_,
expected_properties1);
// Verify shortcut 2.
expected_properties2.set_target(new_exe);
ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_,
expected_properties2);
// |require_args| = false, now both shortcuts are updated.
ASSERT_TRUE(ShellUtil::UpdateShortcuts(
ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_, ShellUtil::CURRENT_USER,
chrome_exe_, /*require_args*/ false, updated_properties));
// Verify shortcut 1.
expected_properties1.set_target(new_exe);
ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_,
expected_properties1);
// Verify shortcut 2.
expected_properties2.set_target(new_exe);
ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_,
expected_properties2);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment