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.
5 #include "chrome/browser/shell_integration.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/macros.h"
13 #include "base/strings/string16.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/test/test_shortcut_win.h"
17 #include "base/win/scoped_com_initializer.h"
18 #include "base/win/windows_version.h"
19 #include "chrome/browser/web_applications/web_app.h"
20 #include "chrome/common/chrome_constants.h"
21 #include "chrome/common/chrome_paths_internal.h"
22 #include "chrome/installer/util/browser_distribution.h"
23 #include "chrome/installer/util/install_util.h"
24 #include "chrome/installer/util/shell_util.h"
25 #include "chrome/installer/util/util_constants.h"
26 #include "testing/gtest/include/gtest/gtest.h"
30 struct ShortcutTestObject
{
32 base::win::ShortcutProperties properties
;
35 class ShellIntegrationWinMigrateShortcutTest
: public testing::Test
{
37 ShellIntegrationWinMigrateShortcutTest()
38 : desired_dual_mode_for_os_version(
39 InstallUtil::ShouldInstallMetroProperties()) {}
41 void SetUp() override
{
42 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
44 // A path to a random target.
45 base::CreateTemporaryFileInDir(temp_dir_
.path(), &other_target_
);
47 // This doesn't need to actually have a base name of "chrome.exe".
48 base::CreateTemporaryFileInDir(temp_dir_
.path(), &chrome_exe_
);
51 ShellUtil::GetBrowserModelId(BrowserDistribution::GetDistribution(),
54 base::FilePath default_user_data_dir
;
55 chrome::GetDefaultUserDataDirectory(&default_user_data_dir
);
56 base::FilePath default_profile_path
=
57 default_user_data_dir
.AppendASCII(chrome::kInitialProfile
);
59 ShellIntegration::GetAppListAppModelIdForProfile(default_profile_path
);
60 non_default_user_data_dir_
= base::FilePath(FILE_PATH_LITERAL("root"))
61 .Append(FILE_PATH_LITERAL("Non Default Data Dir"));
62 non_default_profile_
= L
"NonDefault";
63 non_default_profile_chrome_app_id_
=
64 ShellIntegration::GetChromiumModelIdForProfile(
65 default_user_data_dir
.Append(non_default_profile_
));
66 non_default_user_data_dir_chrome_app_id_
=
67 ShellIntegration::GetChromiumModelIdForProfile(
68 non_default_user_data_dir_
.AppendASCII(chrome::kInitialProfile
));
69 non_default_user_data_dir_and_profile_chrome_app_id_
=
70 ShellIntegration::GetChromiumModelIdForProfile(
71 non_default_user_data_dir_
.Append(non_default_profile_
));
74 extension_id_
= L
"chromiumexampleappidforunittests";
75 base::string16 app_name
=
76 base::UTF8ToUTF16(web_app::GenerateApplicationNameFromExtensionId(
77 base::UTF16ToUTF8(extension_id_
)));
79 ShellIntegration::GetAppModelIdForProfile(app_name
,
80 default_profile_path
);
81 non_default_profile_extension_app_id_
=
82 ShellIntegration::GetAppModelIdForProfile(
84 default_user_data_dir
.Append(non_default_profile_
));
89 // Creates a test shortcut corresponding to |shortcut_properties| and resets
90 // |shortcut_properties| after copying it to an internal structure for later
92 void AddTestShortcutAndResetProperties(
93 base::win::ShortcutProperties
* shortcut_properties
) {
94 ShortcutTestObject shortcut_test_object
;
95 base::FilePath shortcut_path
=
96 temp_dir_
.path().Append(L
"Shortcut " +
97 base::IntToString16(shortcuts_
.size()) +
99 shortcut_test_object
.path
= shortcut_path
;
100 shortcut_test_object
.properties
= *shortcut_properties
;
101 shortcuts_
.push_back(shortcut_test_object
);
102 ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
103 shortcut_path
, *shortcut_properties
,
104 base::win::SHORTCUT_CREATE_ALWAYS
));
105 shortcut_properties
->options
= 0U;
108 void CreateShortcuts() {
109 // A temporary object to pass properties to
110 // AddTestShortcutAndResetProperties().
111 base::win::ShortcutProperties temp_properties
;
113 // Shortcut 0 doesn't point to chrome.exe and thus should never be migrated.
114 temp_properties
.set_target(other_target_
);
115 temp_properties
.set_app_id(L
"Dumbo");
116 ASSERT_NO_FATAL_FAILURE(
117 AddTestShortcutAndResetProperties(&temp_properties
));
119 // Shortcut 1 points to chrome.exe and thus should be migrated.
120 temp_properties
.set_target(chrome_exe_
);
121 temp_properties
.set_app_id(L
"Dumbo");
122 temp_properties
.set_dual_mode(false);
123 ASSERT_NO_FATAL_FAILURE(
124 AddTestShortcutAndResetProperties(&temp_properties
));
126 // Shortcut 2 points to chrome.exe, but already has the right appid and thus
127 // should only be migrated if dual_mode is desired.
128 temp_properties
.set_target(chrome_exe_
);
129 temp_properties
.set_app_id(chrome_app_id_
);
130 ASSERT_NO_FATAL_FAILURE(
131 AddTestShortcutAndResetProperties(&temp_properties
));
133 // Shortcut 3 is like shortcut 1, but it's appid is a prefix of the expected
134 // appid instead of being totally different.
135 base::string16
chrome_app_id_is_prefix(chrome_app_id_
);
136 chrome_app_id_is_prefix
.push_back(L
'1');
137 temp_properties
.set_target(chrome_exe_
);
138 temp_properties
.set_app_id(chrome_app_id_is_prefix
);
139 ASSERT_NO_FATAL_FAILURE(
140 AddTestShortcutAndResetProperties(&temp_properties
));
142 // Shortcut 4 is like shortcut 1, but it's appid is of the same size as the
144 base::string16
same_size_as_chrome_app_id(L
'1', chrome_app_id_
.size());
145 temp_properties
.set_target(chrome_exe_
);
146 temp_properties
.set_app_id(same_size_as_chrome_app_id
);
147 ASSERT_NO_FATAL_FAILURE(
148 AddTestShortcutAndResetProperties(&temp_properties
));
150 // Shortcut 5 doesn't have an app_id, nor is dual_mode even set; they should
151 // be set as expected upon migration.
152 temp_properties
.set_target(chrome_exe_
);
153 ASSERT_NO_FATAL_FAILURE(
154 AddTestShortcutAndResetProperties(&temp_properties
));
156 // Shortcut 6 has a non-default profile directory and so should get a non-
158 temp_properties
.set_target(chrome_exe_
);
159 temp_properties
.set_app_id(L
"Dumbo");
160 temp_properties
.set_arguments(
161 L
"--profile-directory=" + non_default_profile_
);
162 ASSERT_NO_FATAL_FAILURE(
163 AddTestShortcutAndResetProperties(&temp_properties
));
165 // Shortcut 7 has a non-default user data directory and so should get a non-
167 temp_properties
.set_target(chrome_exe_
);
168 temp_properties
.set_app_id(L
"Dumbo");
169 temp_properties
.set_arguments(
170 L
"--user-data-dir=\"" + non_default_user_data_dir_
.value() + L
"\"");
171 ASSERT_NO_FATAL_FAILURE(
172 AddTestShortcutAndResetProperties(&temp_properties
));
174 // Shortcut 8 has a non-default user data directory as well as a non-default
175 // profile directory and so should get a non-default app id.
176 temp_properties
.set_target(chrome_exe_
);
177 temp_properties
.set_app_id(L
"Dumbo");
178 temp_properties
.set_arguments(
179 L
"--user-data-dir=\"" + non_default_user_data_dir_
.value() + L
"\" " +
180 L
"--profile-directory=" + non_default_profile_
);
181 ASSERT_NO_FATAL_FAILURE(
182 AddTestShortcutAndResetProperties(&temp_properties
));
184 // Shortcut 9 is a shortcut to an app and should get an app id for that app
185 // rather than the chrome app id.
186 temp_properties
.set_target(chrome_exe_
);
187 temp_properties
.set_app_id(L
"Dumbo");
188 temp_properties
.set_arguments(
189 L
"--app-id=" + extension_id_
);
190 ASSERT_NO_FATAL_FAILURE(
191 AddTestShortcutAndResetProperties(&temp_properties
));
193 // Shortcut 10 is a shortcut to an app with a non-default profile and should
194 // get an app id for that app with a non-default app id rather than the
196 temp_properties
.set_target(chrome_exe_
);
197 temp_properties
.set_app_id(L
"Dumbo");
198 temp_properties
.set_arguments(
199 L
"--app-id=" + extension_id_
+
200 L
" --profile-directory=" + non_default_profile_
);
201 ASSERT_NO_FATAL_FAILURE(
202 AddTestShortcutAndResetProperties(&temp_properties
));
204 // Shortcut 11 points to chrome.exe, already has the right appid, and has
205 // dual_mode set and thus should only be migrated if dual_mode is checked
206 // and not desired for this OS version.
207 temp_properties
.set_target(chrome_exe_
);
208 temp_properties
.set_app_id(chrome_app_id_
);
209 temp_properties
.set_dual_mode(true);
210 ASSERT_NO_FATAL_FAILURE(
211 AddTestShortcutAndResetProperties(&temp_properties
));
213 // Shortcut 12 is similar to 11 but with dual_mode explicitly set to false.
214 temp_properties
.set_target(chrome_exe_
);
215 temp_properties
.set_app_id(chrome_app_id_
);
216 temp_properties
.set_dual_mode(false);
217 ASSERT_NO_FATAL_FAILURE(
218 AddTestShortcutAndResetProperties(&temp_properties
));
221 base::win::ScopedCOMInitializer com_initializer_
;
223 base::ScopedTempDir temp_dir_
;
226 std::vector
<ShortcutTestObject
> shortcuts_
;
228 // The path to a fake chrome.exe.
229 base::FilePath chrome_exe_
;
231 // The path to a random target.
232 base::FilePath other_target_
;
234 // Chrome's AppUserModelId.
235 base::string16 chrome_app_id_
;
237 // A profile that isn't the Default profile.
238 base::string16 non_default_profile_
;
240 // A user data dir that isn't the default.
241 base::FilePath non_default_user_data_dir_
;
243 // Chrome's AppUserModelId for the non-default profile.
244 base::string16 non_default_profile_chrome_app_id_
;
246 // Chrome's AppUserModelId for the non-default user data dir.
247 base::string16 non_default_user_data_dir_chrome_app_id_
;
249 // Chrome's AppUserModelId for the non-default user data dir and non-default
251 base::string16 non_default_user_data_dir_and_profile_chrome_app_id_
;
253 // The app launcher's app id.
254 base::string16 app_list_app_id_
;
256 // An example extension id of an example app.
257 base::string16 extension_id_
;
259 // The app id of the example app for the default profile and user data dir.
260 base::string16 extension_app_id_
;
262 // The app id of the example app for the non-default profile.
263 base::string16 non_default_profile_extension_app_id_
;
265 // True if the dual mode property should be set for the default chrome
266 // shortcut on the current OS version.
267 const bool desired_dual_mode_for_os_version
;
270 DISALLOW_COPY_AND_ASSIGN(ShellIntegrationWinMigrateShortcutTest
);
275 // Test migration when not checking for dual mode.
276 TEST_F(ShellIntegrationWinMigrateShortcutTest
, DontCheckDualMode
) {
277 if (base::win::GetVersion() < base::win::VERSION_WIN7
)
281 ShellIntegration::MigrateShortcutsInPathInternal(
282 chrome_exe_
, temp_dir_
.path(), false));
284 // Only shortcut 1, 3, 4, 5, 6, 7, 8, 9, and 10 should have been migrated.
285 shortcuts_
[1].properties
.set_app_id(chrome_app_id_
);
286 shortcuts_
[3].properties
.set_app_id(chrome_app_id_
);
287 shortcuts_
[4].properties
.set_app_id(chrome_app_id_
);
288 shortcuts_
[5].properties
.set_app_id(chrome_app_id_
);
289 shortcuts_
[6].properties
.set_app_id(non_default_profile_chrome_app_id_
);
290 shortcuts_
[7].properties
.set_app_id(non_default_user_data_dir_chrome_app_id_
);
291 shortcuts_
[8].properties
.set_app_id(
292 non_default_user_data_dir_and_profile_chrome_app_id_
);
293 shortcuts_
[9].properties
.set_app_id(extension_app_id_
);
294 shortcuts_
[10].properties
.set_app_id(non_default_profile_extension_app_id_
);
296 // Explicitly set the dual_mode expectations on all shortcuts to ensure
297 // ValidateShortcut verifies it.
298 for (size_t i
= 0; i
< shortcuts_
.size(); ++i
) {
299 if (!(shortcuts_
[i
].properties
.options
&
300 base::win::ShortcutProperties::PROPERTIES_DUAL_MODE
)) {
301 shortcuts_
[i
].properties
.set_dual_mode(false);
305 for (size_t i
= 0; i
< shortcuts_
.size(); ++i
) {
307 base::win::ValidateShortcut(shortcuts_
[i
].path
, shortcuts_
[i
].properties
);
310 // Make sure shortcuts are not re-migrated.
312 ShellIntegration::MigrateShortcutsInPathInternal(
313 chrome_exe_
, temp_dir_
.path(), false));
316 // Test migration when also checking for dual mode.
317 TEST_F(ShellIntegrationWinMigrateShortcutTest
, CheckDualMode
) {
318 if (base::win::GetVersion() < base::win::VERSION_WIN7
)
321 // 9 shortcuts should have their app id updated below.
323 // If |desired_dual_mode_for_os_version| is true: shortcut 2 and 13 should
324 // also be migrated to dual_mode for a total of 11 shortcuts migrated.
325 // If |desired_dual_mode_for_os_version| is false: shortcut 11 should
326 // be migrate away from dual_mode for a total of 10 shortcuts migrated.
327 EXPECT_EQ(desired_dual_mode_for_os_version
? 11 : 10,
328 ShellIntegration::MigrateShortcutsInPathInternal(
329 chrome_exe_
, temp_dir_
.path(), true));
331 // Shortcut 1, 3, 4, 5, 6, 7, 8, 9, and 10 should have had both their app_id
332 // fixed and shortcut 1, 2, 3, 4, and 5 should also have had their dual_mode
333 // property added if it is desired (or 11 should have had it removed if it is
335 shortcuts_
[1].properties
.set_app_id(chrome_app_id_
);
336 shortcuts_
[3].properties
.set_app_id(chrome_app_id_
);
337 shortcuts_
[4].properties
.set_app_id(chrome_app_id_
);
338 shortcuts_
[5].properties
.set_app_id(chrome_app_id_
);
339 shortcuts_
[6].properties
.set_app_id(non_default_profile_chrome_app_id_
);
340 shortcuts_
[7].properties
.set_app_id(non_default_user_data_dir_chrome_app_id_
);
341 shortcuts_
[8].properties
.set_app_id(
342 non_default_user_data_dir_and_profile_chrome_app_id_
);
343 shortcuts_
[9].properties
.set_app_id(extension_app_id_
);
344 shortcuts_
[10].properties
.set_app_id(non_default_profile_extension_app_id_
);
346 // Explicitly flag the expected dual_mode properties.
347 shortcuts_
[0].properties
.set_dual_mode(false);
348 if (desired_dual_mode_for_os_version
) {
349 shortcuts_
[1].properties
.set_dual_mode(true);
350 shortcuts_
[2].properties
.set_dual_mode(true);
351 shortcuts_
[3].properties
.set_dual_mode(true);
352 shortcuts_
[4].properties
.set_dual_mode(true);
353 shortcuts_
[5].properties
.set_dual_mode(true);
354 shortcuts_
[12].properties
.set_dual_mode(true);
356 shortcuts_
[11].properties
.set_dual_mode(false);
358 shortcuts_
[6].properties
.set_dual_mode(false);
359 shortcuts_
[7].properties
.set_dual_mode(false);
360 shortcuts_
[8].properties
.set_dual_mode(false);
361 shortcuts_
[9].properties
.set_dual_mode(false);
362 shortcuts_
[10].properties
.set_dual_mode(false);
364 for (size_t i
= 0; i
< shortcuts_
.size(); ++i
) {
366 base::win::ValidateShortcut(shortcuts_
[i
].path
, shortcuts_
[i
].properties
);
369 // Make sure shortcuts are not re-migrated.
371 ShellIntegration::MigrateShortcutsInPathInternal(
372 chrome_exe_
, temp_dir_
.path(), false));
375 TEST(ShellIntegrationWinTest
, GetAppModelIdForProfileTest
) {
376 const base::string16
base_app_id(
377 BrowserDistribution::GetDistribution()->GetBaseAppId());
379 // Empty profile path should get chrome::kBrowserAppID
380 base::FilePath empty_path
;
381 EXPECT_EQ(base_app_id
,
382 ShellIntegration::GetAppModelIdForProfile(base_app_id
, empty_path
));
384 // Default profile path should get chrome::kBrowserAppID
385 base::FilePath default_user_data_dir
;
386 chrome::GetDefaultUserDataDirectory(&default_user_data_dir
);
387 base::FilePath default_profile_path
=
388 default_user_data_dir
.AppendASCII(chrome::kInitialProfile
);
389 EXPECT_EQ(base_app_id
,
390 ShellIntegration::GetAppModelIdForProfile(base_app_id
,
391 default_profile_path
));
393 // Non-default profile path should get chrome::kBrowserAppID joined with
395 base::FilePath
profile_path(FILE_PATH_LITERAL("root"));
396 profile_path
= profile_path
.Append(FILE_PATH_LITERAL("udd"));
397 profile_path
= profile_path
.Append(FILE_PATH_LITERAL("User Data - Test"));
398 EXPECT_EQ(base_app_id
+ L
".udd.UserDataTest",
399 ShellIntegration::GetAppModelIdForProfile(base_app_id
,
403 TEST(ShellIntegrationWinTest
, GetAppListAppModelIdForProfileTest
) {
404 base::string16
base_app_id(
405 BrowserDistribution::GetDistribution()->GetBaseAppId());
406 base_app_id
.append(L
"AppList");
408 // Empty profile path should get chrome::kBrowserAppID + AppList
409 base::FilePath empty_path
;
410 EXPECT_EQ(base_app_id
,
411 ShellIntegration::GetAppListAppModelIdForProfile(empty_path
));
413 // Default profile path should get chrome::kBrowserAppID + AppList
414 base::FilePath default_user_data_dir
;
415 chrome::GetDefaultUserDataDirectory(&default_user_data_dir
);
416 base::FilePath default_profile_path
=
417 default_user_data_dir
.AppendASCII(chrome::kInitialProfile
);
418 EXPECT_EQ(base_app_id
,
419 ShellIntegration::GetAppListAppModelIdForProfile(
420 default_profile_path
));
422 // Non-default profile path should get chrome::kBrowserAppID + AppList joined
423 // with profile info.
424 base::FilePath
profile_path(FILE_PATH_LITERAL("root"));
425 profile_path
= profile_path
.Append(FILE_PATH_LITERAL("udd"));
426 profile_path
= profile_path
.Append(FILE_PATH_LITERAL("User Data - Test"));
427 EXPECT_EQ(base_app_id
+ L
".udd.UserDataTest",
428 ShellIntegration::GetAppListAppModelIdForProfile(profile_path
));