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/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"
28 struct ShortcutTestObject
{
30 base::win::ShortcutProperties properties
;
33 class ShellIntegrationWinMigrateShortcutTest
: public testing::Test
{
35 virtual void SetUp() override
{
36 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
38 // A path to a random target.
39 base::CreateTemporaryFileInDir(temp_dir_
.path(), &other_target_
);
41 // This doesn't need to actually have a base name of "chrome.exe".
42 base::CreateTemporaryFileInDir(temp_dir_
.path(), &chrome_exe_
);
45 ShellUtil::GetBrowserModelId(BrowserDistribution::GetDistribution(),
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
);
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_
));
68 extension_id_
= L
"chromiumexampleappidforunittests";
69 base::string16 app_name
=
70 base::UTF8ToUTF16(web_app::GenerateApplicationNameFromExtensionId(
71 base::UTF16ToUTF8(extension_id_
)));
73 ShellIntegration::GetAppModelIdForProfile(app_name
,
74 default_profile_path
);
75 non_default_profile_extension_app_id_
=
76 ShellIntegration::GetAppModelIdForProfile(
78 default_user_data_dir
.Append(non_default_profile_
));
83 // Creates a test shortcut corresponding to |shortcut_properties| and resets
84 // |shortcut_properties| after copying it to an internal structure for later
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()) +
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;
102 void CreateShortcuts() {
103 // A temporary object to pass properties to
104 // AddTestShortcutAndResetProperties().
105 base::win::ShortcutProperties temp_properties
;
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
));
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
));
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
));
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
));
136 // Shortcut 4 is like shortcut 1, but it's appid is of the same size as the
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
));
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
));
150 // Shortcut 6 has a non-default profile directory and so should get a non-
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
));
159 // Shortcut 7 has a non-default user data directory and so should get a non-
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
));
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
));
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
));
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
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
));
199 base::win::ScopedCOMInitializer com_initializer_
;
201 base::ScopedTempDir temp_dir_
;
204 std::vector
<ShortcutTestObject
> shortcuts_
;
206 // The path to a fake chrome.exe.
207 base::FilePath chrome_exe_
;
209 // The path to a random target.
210 base::FilePath other_target_
;
212 // Chrome's AppUserModelId.
213 base::string16 chrome_app_id_
;
215 // A profile that isn't the Default profile.
216 base::string16 non_default_profile_
;
218 // A user data dir that isn't the default.
219 base::FilePath non_default_user_data_dir_
;
221 // Chrome's AppUserModelId for the non-default profile.
222 base::string16 non_default_profile_chrome_app_id_
;
224 // Chrome's AppUserModelId for the non-default user data dir.
225 base::string16 non_default_user_data_dir_chrome_app_id_
;
227 // Chrome's AppUserModelId for the non-default user data dir and non-default
229 base::string16 non_default_user_data_dir_and_profile_chrome_app_id_
;
231 // The app launcher's app id.
232 base::string16 app_list_app_id_
;
234 // An example extension id of an example app.
235 base::string16 extension_id_
;
237 // The app id of the example app for the default profile and user data dir.
238 base::string16 extension_app_id_
;
240 // The app id of the example app for the non-default profile.
241 base::string16 non_default_profile_extension_app_id_
;
246 // Test migration when not checking for dual mode.
247 TEST_F(ShellIntegrationWinMigrateShortcutTest
, DontCheckDualMode
) {
248 if (base::win::GetVersion() < base::win::VERSION_WIN7
)
252 ShellIntegration::MigrateShortcutsInPathInternal(
253 chrome_exe_
, temp_dir_
.path(), false));
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_
);
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
);
273 // Make sure shortcuts are not re-migrated.
275 ShellIntegration::MigrateShortcutsInPathInternal(
276 chrome_exe_
, temp_dir_
.path(), false));
279 // Test migration when also checking for dual mode.
280 TEST_F(ShellIntegrationWinMigrateShortcutTest
, CheckDualMode
) {
281 if (base::win::GetVersion() < base::win::VERSION_WIN7
)
285 ShellIntegration::MigrateShortcutsInPathInternal(
286 chrome_exe_
, temp_dir_
.path(), true));
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
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_
);
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);
313 for (size_t i
= 0; i
< shortcuts_
.size(); ++i
)
314 base::win::ValidateShortcut(shortcuts_
[i
].path
, shortcuts_
[i
].properties
);
316 // Make sure shortcuts are not re-migrated.
318 ShellIntegration::MigrateShortcutsInPathInternal(
319 chrome_exe_
, temp_dir_
.path(), false));
322 TEST(ShellIntegrationWinTest
, GetAppModelIdForProfileTest
) {
323 const base::string16
base_app_id(
324 BrowserDistribution::GetDistribution()->GetBaseAppId());
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
));
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
));
340 // Non-default profile path should get chrome::kBrowserAppID joined with
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
,
350 TEST(ShellIntegrationWinTest
, GetAppListAppModelIdForProfileTest
) {
351 base::string16
base_app_id(
352 BrowserDistribution::GetDistribution()->GetBaseAppId());
353 base_app_id
.append(L
"AppList");
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
));
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
));
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
));