1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/shell_integration.h" 6 7 #include <vector> 8 9 #include "base/file_util.h" 10 #include "base/files/file_path.h" 11 #include "base/files/scoped_temp_dir.h" 12 #include "base/strings/string16.h" 13 #include "base/strings/string_number_conversions.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "base/test/test_shortcut_win.h" 16 #include "base/win/scoped_com_initializer.h" 17 #include "base/win/windows_version.h" 18 #include "chrome/browser/web_applications/web_app.h" 19 #include "chrome/common/chrome_constants.h" 20 #include "chrome/common/chrome_paths_internal.h" 21 #include "chrome/installer/util/browser_distribution.h" 22 #include "chrome/installer/util/shell_util.h" 23 #include "chrome/installer/util/util_constants.h" 24 #include "testing/gtest/include/gtest/gtest.h" 25 26 namespace { 27 28 struct ShortcutTestObject { 29 base::FilePath path; 30 base::win::ShortcutProperties properties; 31 }; 32 33 class ShellIntegrationWinMigrateShortcutTest : public testing::Test { 34 protected: 35 virtual void SetUp() OVERRIDE { 36 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 37 38 // A path to a random target. 39 base::CreateTemporaryFileInDir(temp_dir_.path(), &other_target_); 40 41 // This doesn't need to actually have a base name of "chrome.exe". 42 base::CreateTemporaryFileInDir(temp_dir_.path(), &chrome_exe_); 43 44 chrome_app_id_ = 45 ShellUtil::GetBrowserModelId(BrowserDistribution::GetDistribution(), 46 true); 47 48 base::FilePath default_user_data_dir; 49 chrome::GetDefaultUserDataDirectory(&default_user_data_dir); 50 base::FilePath default_profile_path = 51 default_user_data_dir.AppendASCII(chrome::kInitialProfile); 52 app_list_app_id_ = 53 ShellIntegration::GetAppListAppModelIdForProfile(default_profile_path); 54 non_default_user_data_dir_ = base::FilePath(FILE_PATH_LITERAL("root")) 55 .Append(FILE_PATH_LITERAL("Non Default Data Dir")); 56 non_default_profile_ = L"NonDefault"; 57 non_default_profile_chrome_app_id_ = 58 ShellIntegration::GetChromiumModelIdForProfile( 59 default_user_data_dir.Append(non_default_profile_)); 60 non_default_user_data_dir_chrome_app_id_ = 61 ShellIntegration::GetChromiumModelIdForProfile( 62 non_default_user_data_dir_.AppendASCII(chrome::kInitialProfile)); 63 non_default_user_data_dir_and_profile_chrome_app_id_ = 64 ShellIntegration::GetChromiumModelIdForProfile( 65 non_default_user_data_dir_.Append(non_default_profile_)); 66 67 68 extension_id_ = L"chromiumexampleappidforunittests"; 69 base::string16 app_name = 70 UTF8ToUTF16(web_app::GenerateApplicationNameFromExtensionId( 71 UTF16ToUTF8(extension_id_))); 72 extension_app_id_ = 73 ShellIntegration::GetAppModelIdForProfile(app_name, 74 default_profile_path); 75 non_default_profile_extension_app_id_ = 76 ShellIntegration::GetAppModelIdForProfile( 77 app_name, 78 default_user_data_dir.Append(non_default_profile_)); 79 80 CreateShortcuts(); 81 } 82 83 // Creates a test shortcut corresponding to |shortcut_properties| and resets 84 // |shortcut_properties| after copying it to an internal structure for later 85 // verification. 86 void AddTestShortcutAndResetProperties( 87 base::win::ShortcutProperties* shortcut_properties) { 88 ShortcutTestObject shortcut_test_object; 89 base::FilePath shortcut_path = 90 temp_dir_.path().Append(L"Shortcut " + 91 base::IntToString16(shortcuts_.size()) + 92 installer::kLnkExt); 93 shortcut_test_object.path = shortcut_path; 94 shortcut_test_object.properties = *shortcut_properties; 95 shortcuts_.push_back(shortcut_test_object); 96 ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink( 97 shortcut_path, *shortcut_properties, 98 base::win::SHORTCUT_CREATE_ALWAYS)); 99 shortcut_properties->options = 0U; 100 } 101 102 void CreateShortcuts() { 103 // A temporary object to pass properties to 104 // AddTestShortcutAndResetProperties(). 105 base::win::ShortcutProperties temp_properties; 106 107 // Shortcut 0 doesn't point to chrome.exe and thus should never be migrated. 108 temp_properties.set_target(other_target_); 109 temp_properties.set_app_id(L"Dumbo"); 110 ASSERT_NO_FATAL_FAILURE( 111 AddTestShortcutAndResetProperties(&temp_properties)); 112 113 // Shortcut 1 points to chrome.exe and thus should be migrated. 114 temp_properties.set_target(chrome_exe_); 115 temp_properties.set_app_id(L"Dumbo"); 116 temp_properties.set_dual_mode(false); 117 ASSERT_NO_FATAL_FAILURE( 118 AddTestShortcutAndResetProperties(&temp_properties)); 119 120 // Shortcut 2 points to chrome.exe, but already has the right appid and thus 121 // should only be migrated if dual_mode is desired. 122 temp_properties.set_target(chrome_exe_); 123 temp_properties.set_app_id(chrome_app_id_); 124 ASSERT_NO_FATAL_FAILURE( 125 AddTestShortcutAndResetProperties(&temp_properties)); 126 127 // Shortcut 3 is like shortcut 1, but it's appid is a prefix of the expected 128 // appid instead of being totally different. 129 base::string16 chrome_app_id_is_prefix(chrome_app_id_); 130 chrome_app_id_is_prefix.push_back(L'1'); 131 temp_properties.set_target(chrome_exe_); 132 temp_properties.set_app_id(chrome_app_id_is_prefix); 133 ASSERT_NO_FATAL_FAILURE( 134 AddTestShortcutAndResetProperties(&temp_properties)); 135 136 // Shortcut 4 is like shortcut 1, but it's appid is of the same size as the 137 // expected appid. 138 base::string16 same_size_as_chrome_app_id(L'1', chrome_app_id_.size()); 139 temp_properties.set_target(chrome_exe_); 140 temp_properties.set_app_id(same_size_as_chrome_app_id); 141 ASSERT_NO_FATAL_FAILURE( 142 AddTestShortcutAndResetProperties(&temp_properties)); 143 144 // Shortcut 5 doesn't have an app_id, nor is dual_mode even set; they should 145 // be set as expected upon migration. 146 temp_properties.set_target(chrome_exe_); 147 ASSERT_NO_FATAL_FAILURE( 148 AddTestShortcutAndResetProperties(&temp_properties)); 149 150 // Shortcut 6 has a non-default profile directory and so should get a non- 151 // default app id. 152 temp_properties.set_target(chrome_exe_); 153 temp_properties.set_app_id(L"Dumbo"); 154 temp_properties.set_arguments( 155 L"--profile-directory=" + non_default_profile_); 156 ASSERT_NO_FATAL_FAILURE( 157 AddTestShortcutAndResetProperties(&temp_properties)); 158 159 // Shortcut 7 has a non-default user data directory and so should get a non- 160 // default app id. 161 temp_properties.set_target(chrome_exe_); 162 temp_properties.set_app_id(L"Dumbo"); 163 temp_properties.set_arguments( 164 L"--user-data-dir=\"" + non_default_user_data_dir_.value() + L"\""); 165 ASSERT_NO_FATAL_FAILURE( 166 AddTestShortcutAndResetProperties(&temp_properties)); 167 168 // Shortcut 8 has a non-default user data directory as well as a non-default 169 // profile directory and so should get a non-default app id. 170 temp_properties.set_target(chrome_exe_); 171 temp_properties.set_app_id(L"Dumbo"); 172 temp_properties.set_arguments( 173 L"--user-data-dir=\"" + non_default_user_data_dir_.value() + L"\" " + 174 L"--profile-directory=" + non_default_profile_); 175 ASSERT_NO_FATAL_FAILURE( 176 AddTestShortcutAndResetProperties(&temp_properties)); 177 178 // Shortcut 9 is a shortcut to an app and should get an app id for that app 179 // rather than the chrome app id. 180 temp_properties.set_target(chrome_exe_); 181 temp_properties.set_app_id(L"Dumbo"); 182 temp_properties.set_arguments( 183 L"--app-id=" + extension_id_); 184 ASSERT_NO_FATAL_FAILURE( 185 AddTestShortcutAndResetProperties(&temp_properties)); 186 187 // Shortcut 10 is a shortcut to an app with a non-default profile and should 188 // get an app id for that app with a non-default app id rather than the 189 // chrome app id. 190 temp_properties.set_target(chrome_exe_); 191 temp_properties.set_app_id(L"Dumbo"); 192 temp_properties.set_arguments( 193 L"--app-id=" + extension_id_ + 194 L" --profile-directory=" + non_default_profile_); 195 ASSERT_NO_FATAL_FAILURE( 196 AddTestShortcutAndResetProperties(&temp_properties)); 197 } 198 199 base::win::ScopedCOMInitializer com_initializer_; 200 201 base::ScopedTempDir temp_dir_; 202 203 // Test shortcuts. 204 std::vector<ShortcutTestObject> shortcuts_; 205 206 // The path to a fake chrome.exe. 207 base::FilePath chrome_exe_; 208 209 // The path to a random target. 210 base::FilePath other_target_; 211 212 // Chrome's AppUserModelId. 213 base::string16 chrome_app_id_; 214 215 // A profile that isn't the Default profile. 216 base::string16 non_default_profile_; 217 218 // A user data dir that isn't the default. 219 base::FilePath non_default_user_data_dir_; 220 221 // Chrome's AppUserModelId for the non-default profile. 222 base::string16 non_default_profile_chrome_app_id_; 223 224 // Chrome's AppUserModelId for the non-default user data dir. 225 base::string16 non_default_user_data_dir_chrome_app_id_; 226 227 // Chrome's AppUserModelId for the non-default user data dir and non-default 228 // profile. 229 base::string16 non_default_user_data_dir_and_profile_chrome_app_id_; 230 231 // The app launcher's app id. 232 base::string16 app_list_app_id_; 233 234 // An example extension id of an example app. 235 base::string16 extension_id_; 236 237 // The app id of the example app for the default profile and user data dir. 238 base::string16 extension_app_id_; 239 240 // The app id of the example app for the non-default profile. 241 base::string16 non_default_profile_extension_app_id_; 242 }; 243 244 } // namespace 245 246 // Test migration when not checking for dual mode. 247 TEST_F(ShellIntegrationWinMigrateShortcutTest, DontCheckDualMode) { 248 if (base::win::GetVersion() < base::win::VERSION_WIN7) 249 return; 250 251 EXPECT_EQ(9, 252 ShellIntegration::MigrateShortcutsInPathInternal( 253 chrome_exe_, temp_dir_.path(), false)); 254 255 // Only shortcut 1, 3, 4, 5, 6, 7, 8, 9, and 10 should have been migrated. 256 shortcuts_[1].properties.set_app_id(chrome_app_id_); 257 shortcuts_[3].properties.set_app_id(chrome_app_id_); 258 shortcuts_[4].properties.set_app_id(chrome_app_id_); 259 shortcuts_[5].properties.set_app_id(chrome_app_id_); 260 shortcuts_[6].properties.set_app_id(non_default_profile_chrome_app_id_); 261 shortcuts_[7].properties.set_app_id(non_default_user_data_dir_chrome_app_id_); 262 shortcuts_[8].properties.set_app_id( 263 non_default_user_data_dir_and_profile_chrome_app_id_); 264 shortcuts_[9].properties.set_app_id(extension_app_id_); 265 shortcuts_[10].properties.set_app_id(non_default_profile_extension_app_id_); 266 267 for (size_t i = 0; i < shortcuts_.size(); ++i) { 268 // Dual mode should be false for all of these. 269 shortcuts_[i].properties.set_dual_mode(false); 270 base::win::ValidateShortcut(shortcuts_[i].path, shortcuts_[i].properties); 271 } 272 273 // Make sure shortcuts are not re-migrated. 274 EXPECT_EQ(0, 275 ShellIntegration::MigrateShortcutsInPathInternal( 276 chrome_exe_, temp_dir_.path(), false)); 277 } 278 279 // Test migration when also checking for dual mode. 280 TEST_F(ShellIntegrationWinMigrateShortcutTest, CheckDualMode) { 281 if (base::win::GetVersion() < base::win::VERSION_WIN7) 282 return; 283 284 EXPECT_EQ(10, 285 ShellIntegration::MigrateShortcutsInPathInternal( 286 chrome_exe_, temp_dir_.path(), true)); 287 288 // Shortcut 1, 3, 4, 5, 6, 7, 8, 9, and 10 should have had both their app_id 289 // fixed and shortcut 1, 2, 3, 4, and 5 should also have had their dual_mode 290 // property fixed. 291 shortcuts_[1].properties.set_app_id(chrome_app_id_); 292 shortcuts_[3].properties.set_app_id(chrome_app_id_); 293 shortcuts_[4].properties.set_app_id(chrome_app_id_); 294 shortcuts_[5].properties.set_app_id(chrome_app_id_); 295 shortcuts_[6].properties.set_app_id(non_default_profile_chrome_app_id_); 296 shortcuts_[7].properties.set_app_id(non_default_user_data_dir_chrome_app_id_); 297 shortcuts_[8].properties.set_app_id( 298 non_default_user_data_dir_and_profile_chrome_app_id_); 299 shortcuts_[9].properties.set_app_id(extension_app_id_); 300 shortcuts_[10].properties.set_app_id(non_default_profile_extension_app_id_); 301 302 shortcuts_[1].properties.set_dual_mode(true); 303 shortcuts_[2].properties.set_dual_mode(true); 304 shortcuts_[3].properties.set_dual_mode(true); 305 shortcuts_[4].properties.set_dual_mode(true); 306 shortcuts_[5].properties.set_dual_mode(true); 307 shortcuts_[6].properties.set_dual_mode(false); 308 shortcuts_[7].properties.set_dual_mode(false); 309 shortcuts_[8].properties.set_dual_mode(false); 310 shortcuts_[9].properties.set_dual_mode(false); 311 shortcuts_[10].properties.set_dual_mode(false); 312 313 for (size_t i = 0; i < shortcuts_.size(); ++i) 314 base::win::ValidateShortcut(shortcuts_[i].path, shortcuts_[i].properties); 315 316 // Make sure shortcuts are not re-migrated. 317 EXPECT_EQ(0, 318 ShellIntegration::MigrateShortcutsInPathInternal( 319 chrome_exe_, temp_dir_.path(), false)); 320 } 321 322 TEST(ShellIntegrationWinTest, GetAppModelIdForProfileTest) { 323 const base::string16 base_app_id( 324 BrowserDistribution::GetDistribution()->GetBaseAppId()); 325 326 // Empty profile path should get chrome::kBrowserAppID 327 base::FilePath empty_path; 328 EXPECT_EQ(base_app_id, 329 ShellIntegration::GetAppModelIdForProfile(base_app_id, empty_path)); 330 331 // Default profile path should get chrome::kBrowserAppID 332 base::FilePath default_user_data_dir; 333 chrome::GetDefaultUserDataDirectory(&default_user_data_dir); 334 base::FilePath default_profile_path = 335 default_user_data_dir.AppendASCII(chrome::kInitialProfile); 336 EXPECT_EQ(base_app_id, 337 ShellIntegration::GetAppModelIdForProfile(base_app_id, 338 default_profile_path)); 339 340 // Non-default profile path should get chrome::kBrowserAppID joined with 341 // profile info. 342 base::FilePath profile_path(FILE_PATH_LITERAL("root")); 343 profile_path = profile_path.Append(FILE_PATH_LITERAL("udd")); 344 profile_path = profile_path.Append(FILE_PATH_LITERAL("User Data - Test")); 345 EXPECT_EQ(base_app_id + L".udd.UserDataTest", 346 ShellIntegration::GetAppModelIdForProfile(base_app_id, 347 profile_path)); 348 } 349 350 TEST(ShellIntegrationWinTest, GetAppListAppModelIdForProfileTest) { 351 base::string16 base_app_id( 352 BrowserDistribution::GetDistribution()->GetBaseAppId()); 353 base_app_id.append(L"AppList"); 354 355 // Empty profile path should get chrome::kBrowserAppID + AppList 356 base::FilePath empty_path; 357 EXPECT_EQ(base_app_id, 358 ShellIntegration::GetAppListAppModelIdForProfile(empty_path)); 359 360 // Default profile path should get chrome::kBrowserAppID + AppList 361 base::FilePath default_user_data_dir; 362 chrome::GetDefaultUserDataDirectory(&default_user_data_dir); 363 base::FilePath default_profile_path = 364 default_user_data_dir.AppendASCII(chrome::kInitialProfile); 365 EXPECT_EQ(base_app_id, 366 ShellIntegration::GetAppListAppModelIdForProfile( 367 default_profile_path)); 368 369 // Non-default profile path should get chrome::kBrowserAppID + AppList joined 370 // with profile info. 371 base::FilePath profile_path(FILE_PATH_LITERAL("root")); 372 profile_path = profile_path.Append(FILE_PATH_LITERAL("udd")); 373 profile_path = profile_path.Append(FILE_PATH_LITERAL("User Data - Test")); 374 EXPECT_EQ(base_app_id + L".udd.UserDataTest", 375 ShellIntegration::GetAppListAppModelIdForProfile(profile_path)); 376 } 377