diff options
author | fdoray <fdoray@chromium.org> | 2016-03-21 10:34:46 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-03-21 17:36:46 +0000 |
commit | 04228a0b711fde48b78491825b13268b851b303b (patch) | |
tree | fbb14cbcad87959e691a4fb96fed0f7a23547eaf | |
parent | 5f5b82dc246c721ceb634983719494519694e57b (diff) | |
download | chromium_src-04228a0b711fde48b78491825b13268b851b303b.zip chromium_src-04228a0b711fde48b78491825b13268b851b303b.tar.gz chromium_src-04228a0b711fde48b78491825b13268b851b303b.tar.bz2 |
Fix the path of shortcuts with an icon in the current install dir.
BUG=595374
Review URL: https://codereview.chromium.org/1800303006
Cr-Commit-Position: refs/heads/master@{#382318}
-rw-r--r-- | chrome/installer/setup/install.cc | 71 | ||||
-rw-r--r-- | chrome/installer/setup/install.h | 9 | ||||
-rw-r--r-- | chrome/installer/setup/install_unittest.cc | 449 | ||||
-rw-r--r-- | chrome/installer/util/install_util.cc | 21 | ||||
-rw-r--r-- | chrome/installer/util/install_util.h | 18 | ||||
-rw-r--r-- | chrome/installer/util/install_util_unittest.cc | 38 |
6 files changed, 455 insertions, 151 deletions
diff --git a/chrome/installer/setup/install.cc b/chrome/installer/setup/install.cc index 744af92..4ec9491 100644 --- a/chrome/installer/setup/install.cc +++ b/chrome/installer/setup/install.cc @@ -253,6 +253,28 @@ installer::InstallStatus InstallNewVersion( return installer::INSTALL_FAILED; } +// Returns the number of components in |file_path|. +size_t GetNumPathComponents(const base::FilePath& file_path) { + std::vector<base::FilePath::StringType> components; + file_path.GetComponents(&components); + return components.size(); +} + +// Returns a path made with the |num_components| first components of +// |file_path|. |file_path| is returned as-is if it contains less than +// |num_components| components. +base::FilePath TruncatePath(const base::FilePath& file_path, + size_t num_components) { + std::vector<base::FilePath::StringType> components; + file_path.GetComponents(&components); + if (components.size() <= num_components) + return file_path; + base::FilePath truncated_file_path; + for (size_t i = 0; i < num_components; ++i) + truncated_file_path = truncated_file_path.Append(components[i]); + return truncated_file_path; +} + } // namespace namespace installer { @@ -261,7 +283,7 @@ void UpdatePerUserShortcutsInLocation( const ShellUtil::ShortcutLocation shortcut_location, BrowserDistribution* dist, const base::FilePath& old_target_dir, - const base::FilePath& old_target_path_suffix, + const base::FilePath& old_target_name_suffix, const base::FilePath& new_target_path) { base::FilePath shortcut_path; const bool get_shortcut_path_return = ShellUtil::GetShortcutPath( @@ -278,18 +300,42 @@ void UpdatePerUserShortcutsInLocation( recursive = true; } + const size_t num_old_target_dir_components = + GetNumPathComponents(old_target_dir); + InstallUtil::ProgramCompare old_target_dir_comparator( + old_target_dir, + InstallUtil::ProgramCompare::ComparisonType::FILE_OR_DIRECTORY); + base::FileEnumerator shortcuts_enum(shortcut_path, recursive, base::FileEnumerator::FILES); for (base::FilePath shortcut = shortcuts_enum.Next(); !shortcut.empty(); shortcut = shortcuts_enum.Next()) { - base::FilePath existing_target_path; - if (!base::win::ResolveShortcut(shortcut, &existing_target_path, nullptr) || - !base::StartsWith(existing_target_path.value(), - old_target_dir.AsEndingWithSeparator().value(), - base::CompareCase::INSENSITIVE_ASCII) || - !base::EndsWith(existing_target_path.value(), - old_target_path_suffix.value(), - base::CompareCase::INSENSITIVE_ASCII)) { + base::win::ShortcutProperties shortcut_properties; + if (!base::win::ResolveShortcutProperties( + shortcut, (base::win::ShortcutProperties::PROPERTIES_TARGET | + base::win::ShortcutProperties::PROPERTIES_ICON), + &shortcut_properties)) { + continue; + } + + if (shortcut_properties.target.ReferencesParent() || + shortcut_properties.icon.ReferencesParent()) { + continue; + } + + // Skip shortcuts whose target isn't a file rooted at |old_target_dir| with + // a name ending in |old_target_name_suffix|. Except for shortcuts whose + // icon is rooted at |old_target_dir|. Note that there can be a false + // negative if the target path or the icon path is a symlink. + // TODO(fdoray): The second condition is only intended to fix Canary + // shortcuts broken by crbug.com/595374, remove it in May 2016. + if (!(old_target_dir_comparator.EvaluatePath(TruncatePath( + shortcut_properties.target, num_old_target_dir_components)) && + base::EndsWith(shortcut_properties.target.BaseName().value(), + old_target_name_suffix.value(), + base::CompareCase::INSENSITIVE_ASCII)) && + !old_target_dir_comparator.EvaluatePath(TruncatePath( + shortcut_properties.icon, num_old_target_dir_components))) { continue; } @@ -464,8 +510,11 @@ void CreateOrUpdateShortcuts( ShellUtil::SHORTCUT_LOCATION_START_MENU_ROOT, dist, start_menu_properties, shortcut_operation); - // Update the target path of existing per-user shortcuts. - if (install_operation == INSTALL_SHORTCUT_REPLACE_EXISTING) { + // Update the target path of existing per-user shortcuts. TODO(fdoray): This + // is only intended to fix Canary shortcuts broken by crbug.com/595374 and + // crbug.com/592040, remove it in May 2016. + if (InstallUtil::IsChromeSxSProcess() && + install_operation == INSTALL_SHORTCUT_REPLACE_EXISTING) { const base::FilePath updated_prefix = target.DirName().DirName(); const base::FilePath updated_suffix = target.BaseName(); diff --git a/chrome/installer/setup/install.h b/chrome/installer/setup/install.h index 9cab9f7..9adb7a9 100644 --- a/chrome/installer/setup/install.h +++ b/chrome/installer/setup/install.h @@ -51,14 +51,15 @@ enum InstallShortcutLevel { }; // Sets |new_target_path| as the new target path of all shortcuts in the -// location specified by |shortcut_location| and |dist| which point to a file: -// - In |old_target_dir| or one of its subdirectories, and, -// - Whose path ends with |old_target_path_suffix|. +// location specified by |shortcut_location| and |dist| which either: +// - Point to a file rooted at |old_target_dir| whose name ends in +// |old_target_name_suffix|, or, +// - Have an icon rooted at |old_target_dir|. void UpdatePerUserShortcutsInLocation( const ShellUtil::ShortcutLocation shortcut_location, BrowserDistribution* dist, const base::FilePath& old_target_dir, - const base::FilePath& old_target_path_suffix, + const base::FilePath& old_target_name_suffix, const base::FilePath& new_target_path); // Escape |att_value| as per the XML AttValue production diff --git a/chrome/installer/setup/install_unittest.cc b/chrome/installer/setup/install_unittest.cc index 44ad121..0eefee2 100644 --- a/chrome/installer/setup/install_unittest.cc +++ b/chrome/installer/setup/install_unittest.cc @@ -13,6 +13,7 @@ #include "base/files/scoped_temp_dir.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "base/path_service.h" #include "base/strings/string16.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" @@ -35,6 +36,12 @@ namespace { +base::FilePath GetNormalizedFilePath(const base::FilePath& path) { + base::FilePath normalized_path; + EXPECT_TRUE(base::NormalizeFilePath(path, &normalized_path)); + return normalized_path; +} + class CreateVisualElementsManifestTest : public testing::Test { protected: void SetUp() override { @@ -70,6 +77,19 @@ class CreateVisualElementsManifestTest : public testing::Test { class InstallShortcutTest : public testing::Test { protected: + struct UpdateShortcutsTestCase { + // Shortcut target path, relative to |temp_dir_|. + const base::FilePath::CharType* target_path; + + // Shortcut icon path, relative to |temp_dir_|. Can be null to create a + // shortcut without an icon. + const base::FilePath::CharType* icon_path; + + // Whether the shortcut's target path should be updated by + // UpdatePerUserShortcutsInLocation(). + bool should_update; + }; + void SetUp() override { EXPECT_EQ(S_OK, CoInitialize(NULL)); @@ -176,6 +196,66 @@ class InstallShortcutTest : public testing::Test { return new installer::MasterPreferences(master_prefs); } + // Creates the shortcuts defined by |test_cases|. Tries to update the target + // path of these shortcuts to |new_target_path_relative| using + // UpdatePerUserShortcutsInLocation(). Verifies that the right shortcuts have + // been updated. + void TestUpdateShortcuts(const UpdateShortcutsTestCase* test_cases, + size_t num_test_cases, + const base::FilePath& new_target_path_relative) { + // Create shortcuts. + for (size_t i = 0; i < num_test_cases; ++i) { + // Make sure that the target exists. + const base::FilePath target_path = + temp_dir_.path().Append(test_cases[i].target_path); + if (!base::PathExists(target_path)) { + ASSERT_TRUE(base::CreateDirectory(target_path.DirName())); + base::File file(target_path, base::File::FLAG_CREATE_ALWAYS | + base::File::FLAG_WRITE); + ASSERT_TRUE(file.IsValid()); + static const char kDummyData[] = "dummy"; + ASSERT_EQ(arraysize(kDummyData), + static_cast<size_t>(file.WriteAtCurrentPos( + kDummyData, arraysize(kDummyData)))); + } + + // Create the shortcut. + base::win::ShortcutProperties properties; + properties.set_target(target_path); + properties.set_icon(temp_dir_.path().Append(test_cases[i].icon_path), 1); + ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink( + user_desktop_shortcut_.InsertBeforeExtension( + base::SizeTToString16(i)), + properties, base::win::SHORTCUT_CREATE_ALWAYS)); + } + + // Update shortcuts. + const base::FilePath new_target_path = + temp_dir_.path().Append(new_target_path_relative); + installer::UpdatePerUserShortcutsInLocation( + ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_, + new_target_path.DirName().DirName(), new_target_path.BaseName(), + new_target_path); + + // Verify that shortcuts were updated correctly. + for (size_t i = 0; i < num_test_cases; ++i) { + base::FilePath target_path; + ASSERT_TRUE(base::win::ResolveShortcut( + user_desktop_shortcut_.InsertBeforeExtension( + base::SizeTToString16(i)), + &target_path, nullptr)); + + if (test_cases[i].should_update) { + EXPECT_EQ(GetNormalizedFilePath(new_target_path), + GetNormalizedFilePath(target_path)); + } else { + EXPECT_EQ(GetNormalizedFilePath( + temp_dir_.path().Append(test_cases[i].target_path)), + GetNormalizedFilePath(target_path)); + } + } + } + base::win::ShortcutProperties expected_properties_; base::win::ShortcutProperties expected_start_menu_properties_; @@ -205,12 +285,6 @@ class InstallShortcutTest : public testing::Test { base::FilePath system_start_menu_subdir_shortcut_; }; -base::FilePath GetNormalizedFilePath(const base::FilePath& path) { - base::FilePath normalized_path; - base::NormalizeFilePath(path, &normalized_path); - return normalized_path; -} - } // namespace // Test that VisualElementsManifest.xml is not created when VisualElements are @@ -466,152 +540,271 @@ TEST_F(InstallShortcutTest, CreateIfNoSystemLevelSomeSystemShortcutsExist) { expected_start_menu_properties_); } -TEST_F(InstallShortcutTest, UpdatePerUserShortcuts) { - static const struct TestCase { - const base::FilePath::CharType* relative_target_path; - bool should_update; - } kTargetPathsToUpdate[] = { - {FILE_PATH_LITERAL("AppData\\Local\\Google\\Chrome " +TEST_F(InstallShortcutTest, UpdatePerUserChromeUserLevelShortcuts) { + static const UpdateShortcutsTestCase kTestCases[] = { + // Shortcut target in the Chrome Canary install directory. No icon. + {FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome " "SxS\\Temp\\scoped_dir\\new_chrome.exe"), - false}, + nullptr, false}, + {FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome " + "SxS\\Temp\\scoped_dir\\chrome.exe"), + nullptr, false}, + {FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome " + "SxS\\Application\\chrome.exe"), + nullptr, false}, + {FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome " + "SxS\\Application\\something_else.exe"), + nullptr, false}, + + // Shortcut target in the user-level Chrome install directory. No icon. + {FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome\\Temp\\scope" + "d_dir\\new_chrome.exe"), + nullptr, true}, + {FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome\\Temp\\scope" + "d_dir\\chrome.exe"), + nullptr, true}, {FILE_PATH_LITERAL( - "AppData\\Local\\Google\\Chrome SxS\\Temp\\scoped_dir\\chrome.exe"), - false}, + "Users\\x\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe"), + nullptr, true}, + {FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome\\Application" + "\\something_else.exe"), + nullptr, false}, + + // Shortcut target in the system-level Chrome install directory. No icon. + {FILE_PATH_LITERAL("Program Files " + "(x86)\\Google\\Chrome\\Temp\\scoped_dir\\new_chrome." + "exe"), + nullptr, false}, + {FILE_PATH_LITERAL( + "Program Files (x86)\\Google\\Chrome\\Temp\\scoped_dir\\chrome.exe"), + nullptr, false}, {FILE_PATH_LITERAL( - "AppData\\Local\\Google\\Chrome SxS\\Application\\chrome.exe"), + "Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"), + nullptr, false}, + {FILE_PATH_LITERAL("Program Files " + "(x86)\\Google\\Chrome\\Application\\something_else." + "exe"), + nullptr, false}, + + // Dummy shortcut target. Icon in the Chrome Canary install directory. + {FILE_PATH_LITERAL("dummy.exe"), + FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome " + "SxS\\Application\\chrome.exe"), false}, - {FILE_PATH_LITERAL("AppData\\Local\\Google\\Chrome " - "SxS\\Application\\something_else.exe"), + {FILE_PATH_LITERAL("dummy.exe"), + FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome " + "SxS\\Application\\User Data\\Profile 1\\Google " + "Profile.ico"), false}, - {FILE_PATH_LITERAL( - "AppData\\Local\\Google\\Chrome\\Temp\\scoped_dir\\new_chrome.exe"), + + // Dummy shortcut target. Icon in the user-level Chrome install directory. + {FILE_PATH_LITERAL("dummy.exe"), + FILE_PATH_LITERAL( + "Users\\x\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe"), true}, - {FILE_PATH_LITERAL( - "AppData\\Local\\Google\\Chrome\\Temp\\scoped_dir\\chrome.exe"), + {FILE_PATH_LITERAL("dummy.exe"), + FILE_PATH_LITERAL( + "Users\\x\\AppData\\Local\\Google\\Chrome\\Application\\User " + "Data\\Profile 1\\Google Profile.ico"), true}, - {FILE_PATH_LITERAL( - "AppData\\Local\\Google\\Chrome\\Application\\chrome.exe"), - true}, - {FILE_PATH_LITERAL( - "AppData\\Local\\Google\\Chrome\\Application\\something_else.exe"), + + // Dummy shortcut target. Icon in the system-level Chrome install + // directory. + {FILE_PATH_LITERAL("dummy.exe"), + FILE_PATH_LITERAL( + "Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"), false}, - {FILE_PATH_LITERAL("something_else.exe"), false}, - }; - // Create shortcuts. - for (size_t i = 0; i < arraysize(kTargetPathsToUpdate); ++i) { - const base::FilePath target_path = - temp_dir_.path().Append(kTargetPathsToUpdate[i].relative_target_path); - ASSERT_TRUE(base::CreateDirectory(target_path.DirName())); - base::File file(target_path, base::File::FLAG_CREATE); - ASSERT_TRUE(file.IsValid()); - - base::win::ShortcutProperties properties; - properties.set_target(target_path); - ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink( - user_desktop_shortcut_.InsertBeforeExtension(base::SizeTToString16(i)), - properties, base::win::SHORTCUT_CREATE_ALWAYS)); - } + // Shortcuts that don't belong to Chrome. + {FILE_PATH_LITERAL("something_else.exe"), nullptr, false}, + {FILE_PATH_LITERAL("something_else.exe"), + FILE_PATH_LITERAL( + "Users\\x\\AppData\\Local\\Google\\Something Else.ico"), + false}, + }; - // Update shortcuts. - const base::FilePath new_target_path = - temp_dir_.path().Append(FILE_PATH_LITERAL( - "AppData\\Local\\Google\\Chrome\\Application\\chrome.exe")); - installer::UpdatePerUserShortcutsInLocation( - ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_, - new_target_path.DirName().DirName(), new_target_path.BaseName(), - new_target_path); - - // Verify that shortcuts were updated correctly. - for (size_t i = 0; i < arraysize(kTargetPathsToUpdate); ++i) { - base::FilePath target_path; - ASSERT_TRUE(base::win::ResolveShortcut( - user_desktop_shortcut_.InsertBeforeExtension(base::SizeTToString16(i)), - &target_path, nullptr)); - - if (kTargetPathsToUpdate[i].should_update) { - EXPECT_EQ(GetNormalizedFilePath(new_target_path), - GetNormalizedFilePath(target_path)); - } else { - EXPECT_EQ(GetNormalizedFilePath(temp_dir_.path().Append( - kTargetPathsToUpdate[i].relative_target_path)), - GetNormalizedFilePath(target_path)); - } - } + TestUpdateShortcuts( + kTestCases, arraysize(kTestCases), + base::FilePath(FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrom" + "e\\Application\\chrome.exe"))); } -TEST_F(InstallShortcutTest, UpdatePerUserShortcutsCanary) { - static const struct TestCase { - const base::FilePath::CharType* relative_target_path; - bool should_update; - } kTargetPathsToUpdate[] = { - {FILE_PATH_LITERAL("AppData\\Local\\Google\\Chrome " +TEST_F(InstallShortcutTest, UpdatePerUserCanaryShortcuts) { + static const UpdateShortcutsTestCase kTestCases[] = { + // Shortcut target in the Chrome Canary install directory. No icon. + {FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome " "SxS\\Temp\\scoped_dir\\new_chrome.exe"), - true}, + nullptr, true}, + {FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome " + "SxS\\Temp\\scoped_dir\\chrome.exe"), + nullptr, true}, + {FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome " + "SxS\\Application\\chrome.exe"), + nullptr, true}, + {FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome " + "SxS\\Application\\something_else.exe"), + nullptr, false}, + + // Shortcut target in the user-level Chrome install directory. No icon. + {FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome\\Temp\\scope" + "d_dir\\new_chrome.exe"), + nullptr, false}, + {FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome\\Temp\\scope" + "d_dir\\chrome.exe"), + nullptr, false}, {FILE_PATH_LITERAL( - "AppData\\Local\\Google\\Chrome SxS\\Temp\\scoped_dir\\chrome.exe"), - true}, + "Users\\x\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe"), + nullptr, false}, + {FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome\\Application" + "\\something_else.exe"), + nullptr, false}, + + // Shortcut target in the system-level Chrome install directory. No icon. + {FILE_PATH_LITERAL("Program Files " + "(x86)\\Google\\Chrome\\Temp\\scoped_dir\\new_chrome." + "exe"), + nullptr, false}, {FILE_PATH_LITERAL( - "AppData\\Local\\Google\\Chrome SxS\\Application\\chrome.exe"), + "Program Files (x86)\\Google\\Chrome\\Temp\\scoped_dir\\chrome.exe"), + nullptr, false}, + {FILE_PATH_LITERAL( + "Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"), + nullptr, false}, + {FILE_PATH_LITERAL("Program Files " + "(x86)\\Google\\Chrome\\Application\\something_else." + "exe"), + nullptr, false}, + + // Dummy shortcut target. Icon in the Chrome Canary install directory. + {FILE_PATH_LITERAL("dummy.exe"), + FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome " + "SxS\\Application\\chrome.exe"), true}, - {FILE_PATH_LITERAL("AppData\\Local\\Google\\Chrome " - "SxS\\Application\\something_else.exe"), + {FILE_PATH_LITERAL("dummy.exe"), + FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome " + "SxS\\Application\\User Data\\Profile 1\\Google " + "Profile.ico"), + true}, + + // Dummy shortcut target. Icon in the user-level Chrome install directory. + {FILE_PATH_LITERAL("dummy.exe"), + FILE_PATH_LITERAL( + "Users\\x\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe"), false}, - {FILE_PATH_LITERAL( - "AppData\\Local\\Google\\Chrome\\Temp\\scoped_dir\\new_chrome.exe"), + {FILE_PATH_LITERAL("dummy.exe"), + FILE_PATH_LITERAL( + "Users\\x\\AppData\\Local\\Google\\Chrome\\Application\\User " + "Data\\Profile 1\\Google Profile.ico"), false}, - {FILE_PATH_LITERAL( - "AppData\\Local\\Google\\Chrome\\Temp\\scoped_dir\\chrome.exe"), + + // Dummy shortcut target. Icon in the system-level Chrome install + // directory. + {FILE_PATH_LITERAL("dummy.exe"), + FILE_PATH_LITERAL( + "Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"), false}, - {FILE_PATH_LITERAL( - "AppData\\Local\\Google\\Chrome\\Application\\chrome.exe"), + + // Shortcuts that don't belong to Chrome. + {FILE_PATH_LITERAL("something_else.exe"), nullptr, false}, + {FILE_PATH_LITERAL("something_else.exe"), + FILE_PATH_LITERAL( + "Users\\x\\AppData\\Local\\Google\\Something Else.ico"), false}, + }; + + TestUpdateShortcuts( + kTestCases, arraysize(kTestCases), + base::FilePath(FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrom" + "e SxS\\Application\\chrome.exe"))); +} + +TEST_F(InstallShortcutTest, UpdatePerUserChromeSystemLevelShortcuts) { + static const UpdateShortcutsTestCase kTestCases[] = { + // Shortcut target in the Chrome Canary install directory. No icon. + {FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome " + "SxS\\Temp\\scoped_dir\\new_chrome.exe"), + nullptr, false}, + {FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome " + "SxS\\Temp\\scoped_dir\\chrome.exe"), + nullptr, false}, + {FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome " + "SxS\\Application\\chrome.exe"), + nullptr, false}, + {FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome " + "SxS\\Application\\something_else.exe"), + nullptr, false}, + + // Shortcut target in the user-level Chrome install directory. No icon. + {FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome\\Temp\\scope" + "d_dir\\new_chrome.exe"), + nullptr, false}, + {FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome\\Temp\\scope" + "d_dir\\chrome.exe"), + nullptr, false}, + {FILE_PATH_LITERAL( + "Users\\x\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe"), + nullptr, false}, + {FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome\\Application" + "\\something_else.exe"), + nullptr, false}, + + // Shortcut target in the system-level Chrome install directory. No icon. + {FILE_PATH_LITERAL("Program Files " + "(x86)\\Google\\Chrome\\Temp\\scoped_dir\\new_chrome." + "exe"), + nullptr, true}, {FILE_PATH_LITERAL( - "AppData\\Local\\Google\\Chrome\\Application\\something_else.exe"), + "Program Files (x86)\\Google\\Chrome\\Temp\\scoped_dir\\chrome.exe"), + nullptr, true}, + {FILE_PATH_LITERAL( + "Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"), + nullptr, true}, + {FILE_PATH_LITERAL("Program Files " + "(x86)\\Google\\Chrome\\Application\\something_else." + "exe"), + nullptr, false}, + + // Dummy shortcut target. Icon in the Chrome Canary install directory. + {FILE_PATH_LITERAL("dummy.exe"), + FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome " + "SxS\\Application\\chrome.exe"), + false}, + {FILE_PATH_LITERAL("dummy.exe"), + FILE_PATH_LITERAL("Users\\x\\AppData\\Local\\Google\\Chrome " + "SxS\\Application\\User Data\\Profile 1\\Google " + "Profile.ico"), false}, - {FILE_PATH_LITERAL("something_else.exe"), false}, - }; - // Create shortcuts. - for (size_t i = 0; i < arraysize(kTargetPathsToUpdate); ++i) { - const base::FilePath target_path = - temp_dir_.path().Append(kTargetPathsToUpdate[i].relative_target_path); - ASSERT_TRUE(base::CreateDirectory(target_path.DirName())); - base::File file(target_path, base::File::FLAG_CREATE); - ASSERT_TRUE(file.IsValid()); - - base::win::ShortcutProperties properties; - properties.set_target(target_path); - ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink( - user_desktop_shortcut_.InsertBeforeExtension(base::SizeTToString16(i)), - properties, base::win::SHORTCUT_CREATE_ALWAYS)); - } + // Dummy shortcut target. Icon in the user-level Chrome install directory. + {FILE_PATH_LITERAL("dummy.exe"), + FILE_PATH_LITERAL( + "Users\\x\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe"), + false}, + {FILE_PATH_LITERAL("dummy.exe"), + FILE_PATH_LITERAL( + "Users\\x\\AppData\\Local\\Google\\Chrome\\Application\\User " + "Data\\Profile 1\\Google Profile.ico"), + false}, - // Update shortcuts. - const base::FilePath new_target_path = - temp_dir_.path().Append(FILE_PATH_LITERAL( - "AppData\\Local\\Google\\Chrome SxS\\Application\\chrome.exe")); - installer::UpdatePerUserShortcutsInLocation( - ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_, - new_target_path.DirName().DirName(), new_target_path.BaseName(), - new_target_path); - - // Verify that shortcuts were updated correctly. - for (size_t i = 0; i < arraysize(kTargetPathsToUpdate); ++i) { - base::FilePath target_path; - ASSERT_TRUE(base::win::ResolveShortcut( - user_desktop_shortcut_.InsertBeforeExtension(base::SizeTToString16(i)), - &target_path, nullptr)); - - if (kTargetPathsToUpdate[i].should_update) { - EXPECT_EQ(GetNormalizedFilePath(new_target_path), - GetNormalizedFilePath(target_path)); - } else { - EXPECT_EQ(GetNormalizedFilePath(temp_dir_.path().Append( - kTargetPathsToUpdate[i].relative_target_path)), - GetNormalizedFilePath(target_path)); - } - } + // Dummy shortcut target. Icon in the system-level Chrome install + // directory. + {FILE_PATH_LITERAL("dummy.exe"), + FILE_PATH_LITERAL( + "Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"), + true}, + + // Shortcuts that don't belong to Chrome. + {FILE_PATH_LITERAL("something_else.exe"), nullptr, false}, + {FILE_PATH_LITERAL("something_else.exe"), + FILE_PATH_LITERAL( + "Users\\x\\AppData\\Local\\Google\\Something Else.ico"), + false}, + }; + + TestUpdateShortcuts( + kTestCases, arraysize(kTestCases), + base::FilePath(FILE_PATH_LITERAL( + "Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"))); } TEST(EscapeXmlAttributeValueTest, EscapeCrazyValue) { diff --git a/chrome/installer/util/install_util.cc b/chrome/installer/util/install_util.cc index 6eb1b66..e894cbb 100644 --- a/chrome/installer/util/install_util.cc +++ b/chrome/installer/util/install_util.cc @@ -609,11 +609,14 @@ base::string16 InstallUtil::GetCurrentDate() { // Open |path| with minimal access to obtain information about it, returning // true and populating |file| on success. -// static bool InstallUtil::ProgramCompare::OpenForInfo(const base::FilePath& path, - base::File* file) { + base::File* file, + ComparisonType comparison_type) { DCHECK(file); - file->Initialize(path, base::File::FLAG_OPEN); + uint32_t flags = base::File::FLAG_OPEN; + if (comparison_type == ComparisonType::FILE_OR_DIRECTORY) + flags |= base::File::FLAG_BACKUP_SEMANTICS; + file->Initialize(path, flags); return file->IsValid(); } @@ -626,10 +629,15 @@ bool InstallUtil::ProgramCompare::GetInfo(const base::File& file, } InstallUtil::ProgramCompare::ProgramCompare(const base::FilePath& path_to_match) + : ProgramCompare(path_to_match, ComparisonType::FILE) {} + +InstallUtil::ProgramCompare::ProgramCompare(const base::FilePath& path_to_match, + ComparisonType comparison_type) : path_to_match_(path_to_match), - file_info_() { + file_info_(), + comparison_type_(comparison_type) { DCHECK(!path_to_match_.empty()); - if (!OpenForInfo(path_to_match_, &file_)) { + if (!OpenForInfo(path_to_match_, &file_, comparison_type_)) { PLOG(WARNING) << "Failed opening " << path_to_match_.value() << "; falling back to path string comparisons."; } else if (!GetInfo(file_, &file_info_)) { @@ -673,8 +681,7 @@ bool InstallUtil::ProgramCompare::EvaluatePath( base::File file; BY_HANDLE_FILE_INFORMATION info = {}; - return (OpenForInfo(path, &file) && - GetInfo(file, &info) && + return (OpenForInfo(path, &file, comparison_type_) && GetInfo(file, &info) && info.dwVolumeSerialNumber == file_info_.dwVolumeSerialNumber && info.nFileIndexHigh == file_info_.nFileIndexHigh && info.nFileIndexLow == file_info_.nFileIndexLow); diff --git a/chrome/installer/util/install_util.h b/chrome/installer/util/install_util.h index 672f0ff..79b8e2a 100644 --- a/chrome/installer/util/install_util.h +++ b/chrome/installer/util/install_util.h @@ -189,19 +189,35 @@ class InstallUtil { // the same file. class ProgramCompare : public RegistryValuePredicate { public: + enum class ComparisonType { + // Evaluation compares existing files. + FILE, + // Evaluation compares existing files or directories. + FILE_OR_DIRECTORY, + }; + + // Constructs a ProgramCompare with FILE as ComparisonType. explicit ProgramCompare(const base::FilePath& path_to_match); + + // Constructs a ProgramCompare with |comparison_type| as ComparisonType. + ProgramCompare(const base::FilePath& path_to_match, + ComparisonType comparison_type); + ~ProgramCompare() override; bool Evaluate(const base::string16& value) const override; bool EvaluatePath(const base::FilePath& path) const; protected: - static bool OpenForInfo(const base::FilePath& path, base::File* file); + static bool OpenForInfo(const base::FilePath& path, + base::File* file, + ComparisonType comparison_type); static bool GetInfo(const base::File& file, BY_HANDLE_FILE_INFORMATION* info); base::FilePath path_to_match_; base::File file_; BY_HANDLE_FILE_INFORMATION file_info_; + ComparisonType comparison_type_; private: DISALLOW_COPY_AND_ASSIGN(ProgramCompare); diff --git a/chrome/installer/util/install_util_unittest.cc b/chrome/installer/util/install_util_unittest.cc index 40467f5..15b7a46 100644 --- a/chrome/installer/util/install_util_unittest.cc +++ b/chrome/installer/util/install_util_unittest.cc @@ -453,6 +453,44 @@ TEST_F(InstallUtilTest, ProgramCompare) { L"\"" + short_expect + L"\"")); } +TEST_F(InstallUtilTest, ProgramCompareWithDirectories) { + base::ScopedTempDir test_dir; + ASSERT_TRUE(test_dir.CreateUniqueTempDir()); + const base::FilePath some_long_dir( + test_dir.path().Append(L"Some Long Directory Name")); + const base::FilePath expect(some_long_dir.Append(L"directory")); + const base::FilePath expect_upcase(some_long_dir.Append(L"DIRECTORY")); + const base::FilePath other(some_long_dir.Append(L"other_directory")); + + ASSERT_TRUE(base::CreateDirectory(some_long_dir)); + ASSERT_TRUE(base::CreateDirectory(expect)); + ASSERT_TRUE(base::CreateDirectory(other)); + + InstallUtil::ProgramCompare program_compare( + expect, InstallUtil::ProgramCompare::ComparisonType::FILE_OR_DIRECTORY); + + // Paths match exactly. + EXPECT_TRUE(program_compare.EvaluatePath(expect)); + // Paths differ by case. + EXPECT_TRUE(program_compare.EvaluatePath(expect_upcase)); + // Paths don't match. + EXPECT_FALSE(program_compare.EvaluatePath(other)); + + // Test where strings don't match, but the same directory is indicated. + std::wstring short_expect; + DWORD short_len = + GetShortPathName(expect.value().c_str(), + base::WriteInto(&short_expect, MAX_PATH), MAX_PATH); + ASSERT_NE(static_cast<DWORD>(0), short_len); + ASSERT_GT(static_cast<DWORD>(MAX_PATH), short_len); + short_expect.resize(short_len); + ASSERT_FALSE( + base::FilePath::CompareEqualIgnoreCase(expect.value(), short_expect)); + EXPECT_TRUE(program_compare.EvaluatePath(expect)); + EXPECT_TRUE(program_compare.EvaluatePath(expect_upcase)); + EXPECT_FALSE(program_compare.EvaluatePath(other)); +} + // Win64 Chrome is always installed in the 32-bit Program Files directory. Test // that IsPerUserInstall returns false for an arbitrary path with // DIR_PROGRAM_FILESX86 as a suffix but not DIR_PROGRAM_FILES when the two are |