summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfdoray <fdoray@chromium.org>2016-03-21 10:34:46 -0700
committerCommit bot <commit-bot@chromium.org>2016-03-21 17:36:46 +0000
commit04228a0b711fde48b78491825b13268b851b303b (patch)
treefbb14cbcad87959e691a4fb96fed0f7a23547eaf
parent5f5b82dc246c721ceb634983719494519694e57b (diff)
downloadchromium_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.cc71
-rw-r--r--chrome/installer/setup/install.h9
-rw-r--r--chrome/installer/setup/install_unittest.cc449
-rw-r--r--chrome/installer/util/install_util.cc21
-rw-r--r--chrome/installer/util/install_util.h18
-rw-r--r--chrome/installer/util/install_util_unittest.cc38
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