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/installer/util/shell_util.h"
6 
7 #include <cguid.h>
8 #include <stddef.h>
9 
10 #include <memory>
11 #include <vector>
12 
13 #include "base/base_paths.h"
14 #include "base/base_paths_win.h"
15 #include "base/command_line.h"
16 #include "base/files/file_enumerator.h"
17 #include "base/files/file_path.h"
18 #include "base/files/file_util.h"
19 #include "base/files/scoped_temp_dir.h"
20 #include "base/stl_util.h"
21 #include "base/strings/string16.h"
22 #include "base/strings/string_util.h"
23 #include "base/synchronization/atomic_flag.h"
24 #include "base/test/scoped_path_override.h"
25 #include "base/test/test_reg_util_win.h"
26 #include "base/test/test_shortcut_win.h"
27 #include "base/win/registry.h"
28 #include "base/win/shortcut.h"
29 #include "base/win/windows_version.h"
30 #include "chrome/install_static/install_util.h"
31 #include "chrome/installer/util/install_util.h"
32 #include "chrome/installer/util/util_constants.h"
33 #include "testing/gtest/include/gtest/gtest.h"
34 
35 namespace {
36 
37 const wchar_t kManganeseExe[] = L"manganese.exe";
38 const wchar_t kIronExe[] = L"iron.exe";
39 const wchar_t kOtherIco[] = L"other.ico";
40 
41 // For registry tests.
42 const wchar_t kTestProgid[] = L"TestApp";
43 const wchar_t kTestOpenCommand[] = L"C:\\test.exe";
44 const wchar_t kTestApplicationName[] = L"Test Application";
45 const wchar_t kTestFileTypeName[] = L"Test File Type";
46 const wchar_t kTestIconPath[] = L"D:\\test.ico";
47 const wchar_t* kTestFileExtensions[] = {
48     L"test1",
49     L"test2",
50 };
51 
52 // TODO(huangs): Separate this into generic shortcut tests and Chrome-specific
53 // tests. Specifically, we should not overly rely on getting shortcut properties
54 // from ShellUtil::AddDefaultShortcutProperties().
55 class ShellUtilShortcutTest : public testing::Test {
56  protected:
ShellUtilShortcutTest()57   ShellUtilShortcutTest() : test_properties_(ShellUtil::CURRENT_USER) {}
58 
SetUp()59   void SetUp() override {
60     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
61     chrome_exe_ = temp_dir_.GetPath().Append(installer::kChromeExe);
62     EXPECT_EQ(0, base::WriteFile(chrome_exe_, "", 0));
63 
64     manganese_exe_ = temp_dir_.GetPath().Append(kManganeseExe);
65     EXPECT_EQ(0, base::WriteFile(manganese_exe_, "", 0));
66 
67     iron_exe_ = temp_dir_.GetPath().Append(kIronExe);
68     EXPECT_EQ(0, base::WriteFile(iron_exe_, "", 0));
69 
70     other_ico_ = temp_dir_.GetPath().Append(kOtherIco);
71     EXPECT_EQ(0, base::WriteFile(other_ico_, "", 0));
72 
73     ASSERT_TRUE(fake_user_desktop_.CreateUniqueTempDir());
74     ASSERT_TRUE(fake_common_desktop_.CreateUniqueTempDir());
75     ASSERT_TRUE(fake_user_quick_launch_.CreateUniqueTempDir());
76     ASSERT_TRUE(fake_default_user_quick_launch_.CreateUniqueTempDir());
77     ASSERT_TRUE(fake_start_menu_.CreateUniqueTempDir());
78     ASSERT_TRUE(fake_common_start_menu_.CreateUniqueTempDir());
79     ASSERT_TRUE(fake_user_startup_.CreateUniqueTempDir());
80     ASSERT_TRUE(fake_common_startup_.CreateUniqueTempDir());
81     user_desktop_override_.reset(new base::ScopedPathOverride(
82         base::DIR_USER_DESKTOP, fake_user_desktop_.GetPath()));
83     common_desktop_override_.reset(new base::ScopedPathOverride(
84         base::DIR_COMMON_DESKTOP, fake_common_desktop_.GetPath()));
85     user_quick_launch_override_.reset(new base::ScopedPathOverride(
86         base::DIR_USER_QUICK_LAUNCH, fake_user_quick_launch_.GetPath()));
87     start_menu_override_.reset(new base::ScopedPathOverride(
88         base::DIR_START_MENU, fake_start_menu_.GetPath()));
89     common_start_menu_override_.reset(new base::ScopedPathOverride(
90         base::DIR_COMMON_START_MENU, fake_common_start_menu_.GetPath()));
91     common_startup_override_.reset(new base::ScopedPathOverride(
92         base::DIR_COMMON_STARTUP, fake_common_startup_.GetPath()));
93     user_startup_override_.reset(new base::ScopedPathOverride(
94         base::DIR_USER_STARTUP, fake_user_startup_.GetPath()));
95 
96     base::FilePath icon_path;
97     base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &icon_path);
98     test_properties_.set_target(chrome_exe_);
99     test_properties_.set_arguments(L"--test --chrome");
100     test_properties_.set_description(L"Makes polar bears dance.");
101     test_properties_.set_icon(icon_path, 7);
102     test_properties_.set_app_id(L"Polar.Bear");
103 
104     // The CLSID below was randomly selected.
105     static constexpr CLSID toast_activator_clsid = {
106         0xb2a1d927,
107         0xacd1,
108         0x484a,
109         {0x82, 0x82, 0xd5, 0xfc, 0x66, 0x56, 0x26, 0x4b}};
110     test_properties_.set_toast_activator_clsid(toast_activator_clsid);
111   }
112 
113   // Returns the expected path of a test shortcut. Returns an empty FilePath on
114   // failure.
GetExpectedShortcutPath(ShellUtil::ShortcutLocation location,const ShellUtil::ShortcutProperties & properties)115   base::FilePath GetExpectedShortcutPath(
116       ShellUtil::ShortcutLocation location,
117       const ShellUtil::ShortcutProperties& properties) {
118     base::FilePath expected_path;
119     switch (location) {
120       case ShellUtil::SHORTCUT_LOCATION_DESKTOP:
121         expected_path = (properties.level == ShellUtil::CURRENT_USER)
122                             ? fake_user_desktop_.GetPath()
123                             : fake_common_desktop_.GetPath();
124         break;
125       case ShellUtil::SHORTCUT_LOCATION_QUICK_LAUNCH:
126         expected_path = fake_user_quick_launch_.GetPath();
127         break;
128       case ShellUtil::SHORTCUT_LOCATION_START_MENU_ROOT:
129         expected_path = (properties.level == ShellUtil::CURRENT_USER)
130                             ? fake_start_menu_.GetPath()
131                             : fake_common_start_menu_.GetPath();
132         break;
133       case ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR_DEPRECATED:
134         expected_path = (properties.level == ShellUtil::CURRENT_USER)
135                             ? fake_start_menu_.GetPath()
136                             : fake_common_start_menu_.GetPath();
137         expected_path = expected_path.Append(
138             InstallUtil::GetChromeShortcutDirNameDeprecated());
139         break;
140       default:
141         ADD_FAILURE() << "Unknown location";
142         return base::FilePath();
143     }
144 
145     base::string16 shortcut_name = properties.has_shortcut_name()
146                                        ? properties.shortcut_name
147                                        : InstallUtil::GetShortcutName();
148     shortcut_name.append(installer::kLnkExt);
149     return expected_path.Append(shortcut_name);
150   }
151 
152   // Validates that the shortcut at |location| matches |properties| (and
153   // implicit default properties) for |dist|.
154   // Note: This method doesn't verify the |pin_to_taskbar| property as it
155   // implies real (non-mocked) state which is flaky to test.
ValidateChromeShortcut(ShellUtil::ShortcutLocation location,const ShellUtil::ShortcutProperties & properties)156   void ValidateChromeShortcut(ShellUtil::ShortcutLocation location,
157                               const ShellUtil::ShortcutProperties& properties) {
158     base::FilePath expected_path(GetExpectedShortcutPath(location, properties));
159 
160     base::win::ShortcutProperties expected_properties;
161     if (properties.has_target()) {
162       expected_properties.set_target(properties.target);
163       expected_properties.set_working_dir(properties.target.DirName());
164     } else {
165       expected_properties.set_target(chrome_exe_);
166       expected_properties.set_working_dir(chrome_exe_.DirName());
167     }
168 
169     if (properties.has_arguments())
170       expected_properties.set_arguments(properties.arguments);
171     else
172       expected_properties.set_arguments(base::string16());
173 
174     if (properties.has_description())
175       expected_properties.set_description(properties.description);
176     else
177       expected_properties.set_description(InstallUtil::GetAppDescription());
178 
179     if (properties.has_icon()) {
180       expected_properties.set_icon(properties.icon, properties.icon_index);
181     } else {
182       int icon_index = install_static::GetIconResourceIndex();
183       expected_properties.set_icon(chrome_exe_, icon_index);
184     }
185 
186     if (properties.has_app_id()) {
187       expected_properties.set_app_id(properties.app_id);
188     } else {
189       // Tests are always seen as user-level installs in ShellUtil.
190       expected_properties.set_app_id(ShellUtil::GetBrowserModelId(true));
191     }
192 
193     if (properties.has_toast_activator_clsid()) {
194       expected_properties.set_toast_activator_clsid(
195           properties.toast_activator_clsid);
196     } else {
197       expected_properties.set_toast_activator_clsid(CLSID_NULL);
198     }
199 
200     base::win::ValidateShortcut(expected_path, expected_properties);
201   }
202 
203   // A ShellUtil::ShortcutProperties object with common properties set already.
204   ShellUtil::ShortcutProperties test_properties_;
205 
206   base::ScopedTempDir temp_dir_;
207   base::ScopedTempDir fake_user_desktop_;
208   base::ScopedTempDir fake_common_desktop_;
209   base::ScopedTempDir fake_user_quick_launch_;
210   base::ScopedTempDir fake_default_user_quick_launch_;
211   base::ScopedTempDir fake_start_menu_;
212   base::ScopedTempDir fake_common_start_menu_;
213   base::ScopedTempDir fake_user_startup_;
214   base::ScopedTempDir fake_common_startup_;
215   std::unique_ptr<base::ScopedPathOverride> user_desktop_override_;
216   std::unique_ptr<base::ScopedPathOverride> common_desktop_override_;
217   std::unique_ptr<base::ScopedPathOverride> user_quick_launch_override_;
218   std::unique_ptr<base::ScopedPathOverride> start_menu_override_;
219   std::unique_ptr<base::ScopedPathOverride> common_start_menu_override_;
220   std::unique_ptr<base::ScopedPathOverride> user_startup_override_;
221   std::unique_ptr<base::ScopedPathOverride> common_startup_override_;
222 
223   base::FilePath chrome_exe_;
224   base::FilePath manganese_exe_;
225   base::FilePath iron_exe_;
226   base::FilePath other_ico_;
227 };
228 
229 }  // namespace
230 
TEST_F(ShellUtilShortcutTest,GetShortcutPath)231 TEST_F(ShellUtilShortcutTest, GetShortcutPath) {
232   base::FilePath path;
233 
234   ShellUtil::GetShortcutPath(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
235                              ShellUtil::CURRENT_USER, &path);
236   EXPECT_EQ(fake_user_desktop_.GetPath(), path);
237 
238   ShellUtil::GetShortcutPath(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
239                              ShellUtil::SYSTEM_LEVEL, &path);
240   EXPECT_EQ(fake_common_desktop_.GetPath(), path);
241 
242   ShellUtil::GetShortcutPath(ShellUtil::SHORTCUT_LOCATION_QUICK_LAUNCH,
243                              ShellUtil::CURRENT_USER, &path);
244   EXPECT_EQ(fake_user_quick_launch_.GetPath(), path);
245 
246   base::string16 start_menu_subfolder =
247       InstallUtil::GetChromeShortcutDirNameDeprecated();
248   ShellUtil::GetShortcutPath(
249       ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR_DEPRECATED,
250       ShellUtil::CURRENT_USER, &path);
251   EXPECT_EQ(fake_start_menu_.GetPath().Append(start_menu_subfolder), path);
252 
253   ShellUtil::GetShortcutPath(
254       ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR_DEPRECATED,
255       ShellUtil::SYSTEM_LEVEL, &path);
256   EXPECT_EQ(fake_common_start_menu_.GetPath().Append(start_menu_subfolder),
257             path);
258 
259   ShellUtil::GetShortcutPath(ShellUtil::SHORTCUT_LOCATION_STARTUP,
260                              ShellUtil::SYSTEM_LEVEL, &path);
261   EXPECT_EQ(fake_common_startup_.GetPath(), path);
262 
263   ShellUtil::GetShortcutPath(ShellUtil::SHORTCUT_LOCATION_STARTUP,
264                              ShellUtil::CURRENT_USER, &path);
265   EXPECT_EQ(fake_user_startup_.GetPath(), path);
266 }
267 
TEST_F(ShellUtilShortcutTest,MoveExistingShortcut)268 TEST_F(ShellUtilShortcutTest, MoveExistingShortcut) {
269   test_properties_.set_shortcut_name(L"Bobo le shortcut");
270   test_properties_.level = ShellUtil::SYSTEM_LEVEL;
271   base::FilePath old_shortcut_path(GetExpectedShortcutPath(
272       ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR_DEPRECATED,
273       test_properties_));
274 
275   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
276       ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR_DEPRECATED,
277       test_properties_, ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
278   ValidateChromeShortcut(
279       ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR_DEPRECATED,
280       test_properties_);
281   ASSERT_TRUE(base::PathExists(old_shortcut_path.DirName()));
282   ASSERT_TRUE(base::PathExists(old_shortcut_path));
283 
284   ASSERT_TRUE(ShellUtil::MoveExistingShortcut(
285       ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR_DEPRECATED,
286       ShellUtil::SHORTCUT_LOCATION_START_MENU_ROOT, test_properties_));
287 
288   ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_START_MENU_ROOT,
289                          test_properties_);
290   ASSERT_FALSE(base::PathExists(old_shortcut_path));
291   ASSERT_FALSE(base::PathExists(old_shortcut_path.DirName()));
292 }
293 
TEST_F(ShellUtilShortcutTest,CreateChromeExeShortcutWithDefaultProperties)294 TEST_F(ShellUtilShortcutTest, CreateChromeExeShortcutWithDefaultProperties) {
295   ShellUtil::ShortcutProperties properties(ShellUtil::CURRENT_USER);
296   ShellUtil::AddDefaultShortcutProperties(chrome_exe_, &properties);
297   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
298       ShellUtil::SHORTCUT_LOCATION_DESKTOP, properties,
299       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
300   ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP, properties);
301 }
302 
TEST_F(ShellUtilShortcutTest,CreateStartMenuShortcutWithAllProperties)303 TEST_F(ShellUtilShortcutTest, CreateStartMenuShortcutWithAllProperties) {
304   test_properties_.set_shortcut_name(L"Bobo le shortcut");
305   test_properties_.level = ShellUtil::SYSTEM_LEVEL;
306   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
307       ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR_DEPRECATED,
308       test_properties_, ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
309   ValidateChromeShortcut(
310       ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR_DEPRECATED,
311       test_properties_);
312 }
313 
TEST_F(ShellUtilShortcutTest,ReplaceSystemLevelDesktopShortcut)314 TEST_F(ShellUtilShortcutTest, ReplaceSystemLevelDesktopShortcut) {
315   test_properties_.level = ShellUtil::SYSTEM_LEVEL;
316   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
317       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
318       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
319 
320   ShellUtil::ShortcutProperties new_properties(ShellUtil::SYSTEM_LEVEL);
321   ShellUtil::AddDefaultShortcutProperties(chrome_exe_, &new_properties);
322   new_properties.set_description(L"New description");
323   new_properties.set_arguments(L"--new-arguments");
324   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
325       ShellUtil::SHORTCUT_LOCATION_DESKTOP, new_properties,
326       ShellUtil::SHELL_SHORTCUT_REPLACE_EXISTING));
327 
328   // Expect the properties set in |new_properties| to be set as above and
329   // properties that don't have a default value to be set back to their default
330   // (as validated in ValidateChromeShortcut()) or unset if they don't .
331   ShellUtil::ShortcutProperties expected_properties(new_properties);
332 
333   ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
334                          expected_properties);
335 }
336 
TEST_F(ShellUtilShortcutTest,UpdateQuickLaunchShortcutArguments)337 TEST_F(ShellUtilShortcutTest, UpdateQuickLaunchShortcutArguments) {
338   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
339       ShellUtil::SHORTCUT_LOCATION_QUICK_LAUNCH, test_properties_,
340       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
341 
342   // Only changing one property, don't need all the defaults.
343   ShellUtil::ShortcutProperties updated_properties(ShellUtil::CURRENT_USER);
344   updated_properties.set_arguments(L"--updated --arguments");
345   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
346       ShellUtil::SHORTCUT_LOCATION_QUICK_LAUNCH, updated_properties,
347       ShellUtil::SHELL_SHORTCUT_UPDATE_EXISTING));
348 
349   // Expect the properties set in |updated_properties| to be set as above and
350   // all other properties to remain unchanged.
351   ShellUtil::ShortcutProperties expected_properties(test_properties_);
352   expected_properties.set_arguments(updated_properties.arguments);
353 
354   ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_QUICK_LAUNCH,
355                          expected_properties);
356 }
357 
TEST_F(ShellUtilShortcutTest,CreateIfNoSystemLevel)358 TEST_F(ShellUtilShortcutTest, CreateIfNoSystemLevel) {
359   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
360       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
361       ShellUtil::SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL));
362   ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
363                          test_properties_);
364 }
365 
TEST_F(ShellUtilShortcutTest,CreateIfNoSystemLevelWithSystemLevelPresent)366 TEST_F(ShellUtilShortcutTest, CreateIfNoSystemLevelWithSystemLevelPresent) {
367   base::string16 shortcut_name(InstallUtil::GetShortcutName() +
368                                installer::kLnkExt);
369 
370   test_properties_.level = ShellUtil::SYSTEM_LEVEL;
371   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
372       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
373       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
374   ASSERT_TRUE(
375       base::PathExists(fake_common_desktop_.GetPath().Append(shortcut_name)));
376 
377   test_properties_.level = ShellUtil::CURRENT_USER;
378   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
379       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
380       ShellUtil::SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL));
381   ASSERT_FALSE(
382       base::PathExists(fake_user_desktop_.GetPath().Append(shortcut_name)));
383 }
384 
TEST_F(ShellUtilShortcutTest,CreateIfNoSystemLevelStartMenu)385 TEST_F(ShellUtilShortcutTest, CreateIfNoSystemLevelStartMenu) {
386   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
387       ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR_DEPRECATED,
388       test_properties_, ShellUtil::SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL));
389   ValidateChromeShortcut(
390       ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR_DEPRECATED,
391       test_properties_);
392 }
393 
TEST_F(ShellUtilShortcutTest,CreateAlwaysUserWithSystemLevelPresent)394 TEST_F(ShellUtilShortcutTest, CreateAlwaysUserWithSystemLevelPresent) {
395   base::string16 shortcut_name(InstallUtil::GetShortcutName() +
396                                installer::kLnkExt);
397 
398   test_properties_.level = ShellUtil::SYSTEM_LEVEL;
399   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
400       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
401       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
402   ASSERT_TRUE(
403       base::PathExists(fake_common_desktop_.GetPath().Append(shortcut_name)));
404 
405   test_properties_.level = ShellUtil::CURRENT_USER;
406   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
407       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
408       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
409   ASSERT_TRUE(
410       base::PathExists(fake_user_desktop_.GetPath().Append(shortcut_name)));
411 }
412 
TEST_F(ShellUtilShortcutTest,RemoveChromeShortcut)413 TEST_F(ShellUtilShortcutTest, RemoveChromeShortcut) {
414   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
415       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
416       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
417   base::FilePath shortcut_path = GetExpectedShortcutPath(
418       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_);
419   ASSERT_TRUE(base::PathExists(shortcut_path));
420 
421   ASSERT_TRUE(ShellUtil::RemoveShortcuts(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
422                                          ShellUtil::CURRENT_USER, chrome_exe_));
423   ASSERT_FALSE(base::PathExists(shortcut_path));
424   ASSERT_TRUE(base::PathExists(shortcut_path.DirName()));
425 }
426 
TEST_F(ShellUtilShortcutTest,RemoveSystemLevelChromeShortcut)427 TEST_F(ShellUtilShortcutTest, RemoveSystemLevelChromeShortcut) {
428   test_properties_.level = ShellUtil::SYSTEM_LEVEL;
429   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
430       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
431       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
432   base::FilePath shortcut_path = GetExpectedShortcutPath(
433       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_);
434   ASSERT_TRUE(base::PathExists(shortcut_path));
435 
436   ASSERT_TRUE(ShellUtil::RemoveShortcuts(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
437                                          ShellUtil::SYSTEM_LEVEL, chrome_exe_));
438   ASSERT_FALSE(base::PathExists(shortcut_path));
439   ASSERT_TRUE(base::PathExists(shortcut_path.DirName()));
440 }
441 
TEST_F(ShellUtilShortcutTest,RemoveMultipleChromeShortcuts)442 TEST_F(ShellUtilShortcutTest, RemoveMultipleChromeShortcuts) {
443   // Shortcut 1: targets "chrome.exe"; no arguments.
444   test_properties_.set_shortcut_name(L"Chrome 1");
445   test_properties_.set_arguments(L"");
446   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
447       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
448       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
449   base::FilePath shortcut1_path = GetExpectedShortcutPath(
450       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_);
451   ASSERT_TRUE(base::PathExists(shortcut1_path));
452 
453   // Shortcut 2: targets "chrome.exe"; has arguments; icon set to "other.ico".
454   test_properties_.set_shortcut_name(L"Chrome 2");
455   test_properties_.set_arguments(L"--profile-directory=\"Profile 2\"");
456   test_properties_.set_icon(other_ico_, 0);
457   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
458       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
459       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
460   base::FilePath shortcut2_path = GetExpectedShortcutPath(
461       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_);
462   ASSERT_TRUE(base::PathExists(shortcut2_path));
463 
464   // Shortcut 3: targets "iron.exe"; has arguments; icon set to "chrome.exe".
465   test_properties_.set_shortcut_name(L"Iron 3");
466   test_properties_.set_target(iron_exe_);
467   test_properties_.set_icon(chrome_exe_, 3);
468   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
469       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
470       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
471   base::FilePath shortcut3_path = GetExpectedShortcutPath(
472       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_);
473   ASSERT_TRUE(base::PathExists(shortcut3_path));
474 
475   // Remove shortcuts that target "chrome.exe".
476   ASSERT_TRUE(ShellUtil::RemoveShortcuts(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
477                                          ShellUtil::CURRENT_USER, chrome_exe_));
478   ASSERT_FALSE(base::PathExists(shortcut1_path));
479   ASSERT_FALSE(base::PathExists(shortcut2_path));
480   ASSERT_TRUE(base::PathExists(shortcut3_path));
481   ASSERT_TRUE(base::PathExists(shortcut1_path.DirName()));
482 }
483 
TEST_F(ShellUtilShortcutTest,RetargetShortcutsWithArgs)484 TEST_F(ShellUtilShortcutTest, RetargetShortcutsWithArgs) {
485   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
486       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
487       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
488   ASSERT_TRUE(base::PathExists(GetExpectedShortcutPath(
489       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_)));
490 
491   base::FilePath new_exe = manganese_exe_;
492   // Relies on the fact that |test_properties_| has non-empty arguments.
493   ASSERT_TRUE(ShellUtil::RetargetShortcutsWithArgs(
494       ShellUtil::SHORTCUT_LOCATION_DESKTOP, ShellUtil::CURRENT_USER,
495       chrome_exe_, new_exe));
496 
497   ShellUtil::ShortcutProperties expected_properties(test_properties_);
498   expected_properties.set_target(new_exe);
499   ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
500                          expected_properties);
501 }
502 
TEST_F(ShellUtilShortcutTest,RetargetSystemLevelChromeShortcutsWithArgs)503 TEST_F(ShellUtilShortcutTest, RetargetSystemLevelChromeShortcutsWithArgs) {
504   test_properties_.level = ShellUtil::SYSTEM_LEVEL;
505   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
506       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
507       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
508   ASSERT_TRUE(base::PathExists(GetExpectedShortcutPath(
509       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_)));
510 
511   base::FilePath new_exe = manganese_exe_;
512   // Relies on the fact that |test_properties_| has non-empty arguments.
513   ASSERT_TRUE(ShellUtil::RetargetShortcutsWithArgs(
514       ShellUtil::SHORTCUT_LOCATION_DESKTOP, ShellUtil::SYSTEM_LEVEL,
515       chrome_exe_, new_exe));
516 
517   ShellUtil::ShortcutProperties expected_properties(test_properties_);
518   expected_properties.set_target(new_exe);
519   ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
520                          expected_properties);
521 }
522 
TEST_F(ShellUtilShortcutTest,RetargetChromeShortcutsWithArgsEmpty)523 TEST_F(ShellUtilShortcutTest, RetargetChromeShortcutsWithArgsEmpty) {
524   // Shortcut 1: targets "chrome.exe"; no arguments.
525   test_properties_.set_shortcut_name(L"Chrome 1");
526   test_properties_.set_arguments(L"");
527   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
528       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
529       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
530   ASSERT_TRUE(base::PathExists(GetExpectedShortcutPath(
531       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_)));
532   ShellUtil::ShortcutProperties expected_properties1(test_properties_);
533 
534   // Shortcut 2: targets "chrome.exe"; has arguments.
535   test_properties_.set_shortcut_name(L"Chrome 2");
536   test_properties_.set_arguments(L"--profile-directory=\"Profile 2\"");
537   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
538       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
539       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
540   ASSERT_TRUE(base::PathExists(GetExpectedShortcutPath(
541       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_)));
542   ShellUtil::ShortcutProperties expected_properties2(test_properties_);
543 
544   // Retarget shortcuts: replace "chrome.exe" with "manganese.exe". Only
545   // shortcuts with non-empty arguments (i.e., shortcut 2) gets updated.
546   base::FilePath new_exe = manganese_exe_;
547   ASSERT_TRUE(ShellUtil::RetargetShortcutsWithArgs(
548       ShellUtil::SHORTCUT_LOCATION_DESKTOP, ShellUtil::CURRENT_USER,
549       chrome_exe_, new_exe));
550 
551   // Verify shortcut 1: no change.
552   ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
553                          expected_properties1);
554 
555   // Verify shortcut 2: target => "manganese.exe".
556   expected_properties2.set_target(new_exe);
557   ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
558                          expected_properties2);
559 }
560 
TEST_F(ShellUtilShortcutTest,RetargetChromeShortcutsWithArgsIcon)561 TEST_F(ShellUtilShortcutTest, RetargetChromeShortcutsWithArgsIcon) {
562   const int kTestIconIndex1 = 3;
563   const int kTestIconIndex2 = 5;
564   const int kTestIconIndex3 = 8;
565 
566   // Shortcut 1: targets "chrome.exe"; icon same as target.
567   test_properties_.set_shortcut_name(L"Chrome 1");
568   test_properties_.set_icon(test_properties_.target, kTestIconIndex1);
569   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
570       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
571       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
572   ASSERT_TRUE(base::PathExists(GetExpectedShortcutPath(
573       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_)));
574   ShellUtil::ShortcutProperties expected_properties1(test_properties_);
575 
576   // Shortcut 2: targets "chrome.exe"; icon set to "other.ico".
577   test_properties_.set_shortcut_name(L"Chrome 2");
578   test_properties_.set_icon(other_ico_, kTestIconIndex2);
579   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
580       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
581       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
582   ASSERT_TRUE(base::PathExists(GetExpectedShortcutPath(
583       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_)));
584   ShellUtil::ShortcutProperties expected_properties2(test_properties_);
585 
586   // Shortcut 3: targets "iron.exe"; icon set to "chrome.exe".
587   test_properties_.set_target(iron_exe_);
588   test_properties_.set_shortcut_name(L"Iron 3");
589   test_properties_.set_icon(chrome_exe_, kTestIconIndex3);
590   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
591       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
592       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
593   ASSERT_TRUE(base::PathExists(GetExpectedShortcutPath(
594       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_)));
595   ShellUtil::ShortcutProperties expected_properties3(test_properties_);
596 
597   // Retarget shortcuts: replace "chrome.exe" with "manganese.exe".
598   // Relies on the fact that |test_properties_| has non-empty arguments.
599   base::FilePath new_exe = manganese_exe_;
600   ASSERT_TRUE(ShellUtil::RetargetShortcutsWithArgs(
601       ShellUtil::SHORTCUT_LOCATION_DESKTOP, ShellUtil::CURRENT_USER,
602       chrome_exe_, new_exe));
603 
604   // Verify shortcut 1: target & icon => "manganese.exe", kept same icon index.
605   expected_properties1.set_target(new_exe);
606   expected_properties1.set_icon(new_exe, kTestIconIndex1);
607   ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
608                          expected_properties1);
609 
610   // Verify shortcut 2: target => "manganese.exe", kept icon.
611   expected_properties2.set_target(new_exe);
612   ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
613                          expected_properties2);
614 
615   // Verify shortcut 3: no change, since target doesn't match.
616   ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
617                          expected_properties3);
618 }
619 
TEST_F(ShellUtilShortcutTest,ClearShortcutArguments)620 TEST_F(ShellUtilShortcutTest, ClearShortcutArguments) {
621   // Shortcut 1: targets "chrome.exe"; no arguments.
622   test_properties_.set_shortcut_name(L"Chrome 1");
623   test_properties_.set_arguments(L"");
624   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
625       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
626       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
627   base::FilePath shortcut1_path = GetExpectedShortcutPath(
628       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_);
629   ASSERT_TRUE(base::PathExists(shortcut1_path));
630   ShellUtil::ShortcutProperties expected_properties1(test_properties_);
631 
632   // Shortcut 2: targets "chrome.exe"; has 1 argument in the allow list.
633   test_properties_.set_shortcut_name(L"Chrome 2");
634   test_properties_.set_arguments(L"--profile-directory=\"Profile 2\"");
635   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
636       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
637       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
638   base::FilePath shortcut2_path = GetExpectedShortcutPath(
639       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_);
640   ASSERT_TRUE(base::PathExists(shortcut2_path));
641   ShellUtil::ShortcutProperties expected_properties2(test_properties_);
642 
643   // Shortcut 3: targets "chrome.exe"; has an unknown argument.
644   test_properties_.set_shortcut_name(L"Chrome 3");
645   test_properties_.set_arguments(L"foo.com");
646   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
647       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
648       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
649   base::FilePath shortcut3_path = GetExpectedShortcutPath(
650       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_);
651   ASSERT_TRUE(base::PathExists(shortcut3_path));
652   ShellUtil::ShortcutProperties expected_properties3(test_properties_);
653 
654   // Shortcut 4: targets "chrome.exe"; has both unknown and known arguments.
655   const base::string16 kKnownArg = L"--app-id";
656   const base::string16 kExpectedArgs = L"foo.com " + kKnownArg;
657   test_properties_.set_shortcut_name(L"Chrome 4");
658   test_properties_.set_arguments(kExpectedArgs);
659   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
660       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
661       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
662   base::FilePath shortcut4_path = GetExpectedShortcutPath(
663       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_);
664   ASSERT_TRUE(base::PathExists(shortcut4_path));
665   ShellUtil::ShortcutProperties expected_properties4(test_properties_);
666 
667   // List the shortcuts.
668   std::vector<std::pair<base::FilePath, base::string16>> shortcuts;
669   EXPECT_TRUE(ShellUtil::ShortcutListMaybeRemoveUnknownArgs(
670       ShellUtil::SHORTCUT_LOCATION_DESKTOP, ShellUtil::CURRENT_USER,
671       chrome_exe_, false, nullptr, &shortcuts));
672   ASSERT_EQ(2u, shortcuts.size());
673   std::pair<base::FilePath, base::string16> shortcut3 =
674       shortcuts[0].first == shortcut3_path ? shortcuts[0] : shortcuts[1];
675   std::pair<base::FilePath, base::string16> shortcut4 =
676       shortcuts[0].first == shortcut4_path ? shortcuts[0] : shortcuts[1];
677   EXPECT_EQ(shortcut3_path, shortcut3.first);
678   EXPECT_EQ(L"foo.com", shortcut3.second);
679   EXPECT_EQ(shortcut4_path, shortcut4.first);
680   EXPECT_EQ(kExpectedArgs, shortcut4.second);
681 
682   // Clear shortcuts.
683   shortcuts.clear();
684   EXPECT_TRUE(ShellUtil::ShortcutListMaybeRemoveUnknownArgs(
685       ShellUtil::SHORTCUT_LOCATION_DESKTOP, ShellUtil::CURRENT_USER,
686       chrome_exe_, true, nullptr, &shortcuts));
687   ASSERT_EQ(2u, shortcuts.size());
688   shortcut3 =
689       shortcuts[0].first == shortcut3_path ? shortcuts[0] : shortcuts[1];
690   shortcut4 =
691       shortcuts[0].first == shortcut4_path ? shortcuts[0] : shortcuts[1];
692   EXPECT_EQ(shortcut3_path, shortcut3.first);
693   EXPECT_EQ(L"foo.com", shortcut3.second);
694   EXPECT_EQ(shortcut4_path, shortcut4.first);
695   EXPECT_EQ(kExpectedArgs, shortcut4.second);
696 
697   ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
698                          expected_properties1);
699   ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
700                          expected_properties2);
701   expected_properties3.set_arguments(base::string16());
702   ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
703                          expected_properties3);
704   expected_properties4.set_arguments(kKnownArg);
705   ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
706                          expected_properties4);
707 }
708 
TEST_F(ShellUtilShortcutTest,ShortcutsAreNotHidden)709 TEST_F(ShellUtilShortcutTest, ShortcutsAreNotHidden) {
710   // Shortcut 1: targets "chrome.exe"; not hidden.
711   test_properties_.set_shortcut_name(L"Chrome Visible");
712   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
713       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
714       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
715   base::FilePath visible_shortcut = GetExpectedShortcutPath(
716       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_);
717   ASSERT_TRUE(base::PathExists(visible_shortcut));
718 
719   // Shortcut 2: targets "chrome.exe"; hidden.
720   test_properties_.set_shortcut_name(L"Chrome Hidden");
721   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
722       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
723       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
724   base::FilePath hidden_shortcut = GetExpectedShortcutPath(
725       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_);
726   ASSERT_TRUE(base::PathExists(hidden_shortcut));
727   DWORD shortcut_attributes =
728       ::GetFileAttributes(hidden_shortcut.value().c_str());
729   ASSERT_NE(shortcut_attributes, INVALID_FILE_ATTRIBUTES);
730   ASSERT_TRUE(::SetFileAttributes(hidden_shortcut.value().c_str(),
731                                   shortcut_attributes | FILE_ATTRIBUTE_HIDDEN));
732 
733   EXPECT_TRUE(ShellUtil::ResetShortcutFileAttributes(
734       ShellUtil::SHORTCUT_LOCATION_DESKTOP, ShellUtil::CURRENT_USER,
735       chrome_exe_));
736 
737   shortcut_attributes = ::GetFileAttributes(visible_shortcut.value().c_str());
738   EXPECT_EQ(shortcut_attributes & FILE_ATTRIBUTE_HIDDEN, 0UL);
739   shortcut_attributes = ::GetFileAttributes(hidden_shortcut.value().c_str());
740   EXPECT_EQ(shortcut_attributes & FILE_ATTRIBUTE_HIDDEN, 0UL);
741 }
742 
TEST_F(ShellUtilShortcutTest,CreateMultipleStartMenuShortcutsAndRemoveFolder)743 TEST_F(ShellUtilShortcutTest, CreateMultipleStartMenuShortcutsAndRemoveFolder) {
744   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
745       ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR_DEPRECATED,
746       test_properties_, ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
747   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
748       ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR, test_properties_,
749       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
750   test_properties_.set_shortcut_name(L"A second shortcut");
751   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
752       ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR_DEPRECATED,
753       test_properties_, ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
754   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
755       ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR, test_properties_,
756       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
757 
758   base::FilePath chrome_shortcut_folder(fake_start_menu_.GetPath().Append(
759       InstallUtil::GetChromeShortcutDirNameDeprecated()));
760   base::FilePath chrome_apps_shortcut_folder(fake_start_menu_.GetPath().Append(
761       InstallUtil::GetChromeAppsShortcutDirName()));
762 
763   base::FileEnumerator chrome_file_counter(chrome_shortcut_folder, false,
764                                            base::FileEnumerator::FILES);
765   int count = 0;
766   while (!chrome_file_counter.Next().empty())
767     ++count;
768   EXPECT_EQ(2, count);
769 
770   base::FileEnumerator chrome_apps_file_counter(
771       chrome_apps_shortcut_folder, false, base::FileEnumerator::FILES);
772   count = 0;
773   while (!chrome_apps_file_counter.Next().empty())
774     ++count;
775   EXPECT_EQ(2, count);
776 
777   ASSERT_TRUE(base::PathExists(chrome_shortcut_folder));
778   ASSERT_TRUE(ShellUtil::RemoveShortcuts(
779       ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR_DEPRECATED,
780       ShellUtil::CURRENT_USER, chrome_exe_));
781   ASSERT_FALSE(base::PathExists(chrome_shortcut_folder));
782 
783   ASSERT_TRUE(base::PathExists(chrome_apps_shortcut_folder));
784   ASSERT_TRUE(ShellUtil::RemoveShortcuts(
785       ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR,
786       ShellUtil::CURRENT_USER, chrome_exe_));
787   ASSERT_FALSE(base::PathExists(chrome_apps_shortcut_folder));
788 }
789 
TEST_F(ShellUtilShortcutTest,DeleteStartMenuRootShortcutWithoutRemovingFolder)790 TEST_F(ShellUtilShortcutTest,
791        DeleteStartMenuRootShortcutWithoutRemovingFolder) {
792   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
793       ShellUtil::SHORTCUT_LOCATION_START_MENU_ROOT, test_properties_,
794       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
795 
796   base::string16 shortcut_name(InstallUtil::GetShortcutName() +
797                                installer::kLnkExt);
798   base::FilePath shortcut_path(
799       fake_start_menu_.GetPath().Append(shortcut_name));
800 
801   ASSERT_TRUE(base::PathExists(fake_start_menu_.GetPath()));
802   ASSERT_TRUE(base::PathExists(shortcut_path));
803   ASSERT_TRUE(
804       ShellUtil::RemoveShortcuts(ShellUtil::SHORTCUT_LOCATION_START_MENU_ROOT,
805                                  ShellUtil::CURRENT_USER, chrome_exe_));
806   // The shortcut should be removed but the "Start Menu" root directory should
807   // remain.
808   ASSERT_TRUE(base::PathExists(fake_start_menu_.GetPath()));
809   ASSERT_FALSE(base::PathExists(shortcut_path));
810 }
811 
TEST_F(ShellUtilShortcutTest,DontRemoveChromeShortcutIfPointsToAnotherChrome)812 TEST_F(ShellUtilShortcutTest, DontRemoveChromeShortcutIfPointsToAnotherChrome) {
813   base::ScopedTempDir other_exe_dir;
814   ASSERT_TRUE(other_exe_dir.CreateUniqueTempDir());
815   base::FilePath other_chrome_exe =
816       other_exe_dir.GetPath().Append(installer::kChromeExe);
817   EXPECT_EQ(0, base::WriteFile(other_chrome_exe, "", 0));
818 
819   test_properties_.set_target(other_chrome_exe);
820   ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut(
821       ShellUtil::SHORTCUT_LOCATION_DESKTOP, test_properties_,
822       ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS));
823 
824   base::string16 shortcut_name(InstallUtil::GetShortcutName() +
825                                installer::kLnkExt);
826   base::FilePath shortcut_path(
827       fake_user_desktop_.GetPath().Append(shortcut_name));
828   ASSERT_TRUE(base::PathExists(shortcut_path));
829 
830   // The shortcut shouldn't be removed as it was installed pointing to
831   // |other_chrome_exe| and RemoveChromeShortcut() is being told that the
832   // removed shortcut should point to |chrome_exe_|.
833   ASSERT_TRUE(ShellUtil::RemoveShortcuts(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
834                                          ShellUtil::CURRENT_USER, chrome_exe_));
835   ASSERT_TRUE(base::PathExists(shortcut_path));
836   ASSERT_TRUE(base::PathExists(shortcut_path.DirName()));
837 }
838 
839 class ShellUtilRegistryTest : public testing::Test {
840  public:
ShellUtilRegistryTest()841   ShellUtilRegistryTest() {}
842 
843  protected:
SetUp()844   void SetUp() override {
845     ASSERT_NO_FATAL_FAILURE(
846         registry_overrides_.OverrideRegistry(HKEY_CURRENT_USER));
847 
848     // .test2 files already have a default application.
849     base::win::RegKey key;
850     ASSERT_EQ(ERROR_SUCCESS,
851               key.Create(HKEY_CURRENT_USER, L"Software\\Classes\\.test2",
852                          KEY_ALL_ACCESS));
853     EXPECT_EQ(ERROR_SUCCESS, key.WriteValue(L"", L"SomeOtherApp"));
854   }
855 
OpenCommand()856   static base::CommandLine OpenCommand() {
857     base::FilePath open_command_path(kTestOpenCommand);
858     base::CommandLine open_command(open_command_path);
859     return open_command;
860   }
861 
FileExtensions()862   static std::set<base::string16> FileExtensions() {
863     std::set<base::string16> file_extensions;
864     for (size_t i = 0; i < base::size(kTestFileExtensions); ++i)
865       file_extensions.insert(kTestFileExtensions[i]);
866     return file_extensions;
867   }
868 
869  private:
870   registry_util::RegistryOverrideManager registry_overrides_;
871 
872   DISALLOW_COPY_AND_ASSIGN(ShellUtilRegistryTest);
873 };
874 
TEST_F(ShellUtilRegistryTest,AddFileAssociations)875 TEST_F(ShellUtilRegistryTest, AddFileAssociations) {
876   // Create file associations.
877   EXPECT_TRUE(ShellUtil::AddFileAssociations(
878       kTestProgid, OpenCommand(), kTestApplicationName, kTestFileTypeName,
879       base::FilePath(kTestIconPath), FileExtensions()));
880 
881   // Ensure that the registry keys have been correctly set.
882   base::win::RegKey key;
883   std::wstring value;
884   ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER,
885                                     L"Software\\Classes\\TestApp", KEY_READ));
886   EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"", &value));
887   EXPECT_EQ(L"Test File Type", value);
888   EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"FileExtensions", &value));
889   EXPECT_EQ(L".test1;.test2", value);
890   ASSERT_EQ(ERROR_SUCCESS,
891             key.Open(HKEY_CURRENT_USER,
892                      L"Software\\Classes\\TestApp\\DefaultIcon", KEY_READ));
893   EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"", &value));
894   EXPECT_EQ(L"D:\\test.ico,0", value);
895   ASSERT_EQ(
896       ERROR_SUCCESS,
897       key.Open(HKEY_CURRENT_USER,
898                L"Software\\Classes\\TestApp\\shell\\open\\command", KEY_READ));
899   EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"", &value));
900   EXPECT_EQ(L"\"C:\\test.exe\" --single-argument %1", value);
901 
902   // The Application subkey and values are only required by Windows 8 and later.
903   if (base::win::GetVersion() >= base::win::Version::WIN8) {
904     ASSERT_EQ(ERROR_SUCCESS,
905               key.Open(HKEY_CURRENT_USER,
906                        L"Software\\Classes\\TestApp\\Application", KEY_READ));
907     EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"ApplicationName", &value));
908     EXPECT_EQ(L"Test Application", value);
909     EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"ApplicationIcon", &value));
910     EXPECT_EQ(L"D:\\test.ico,0", value);
911   }
912 
913   // .test1 should be default-associated with our test app.
914   ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER,
915                                     L"Software\\Classes\\.test1", KEY_READ));
916   EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"", &value));
917   EXPECT_EQ(L"TestApp", value);
918   ASSERT_EQ(ERROR_SUCCESS,
919             key.Open(HKEY_CURRENT_USER,
920                      L"Software\\Classes\\.test1\\OpenWithProgids", KEY_READ));
921   EXPECT_TRUE(key.HasValue(L"TestApp"));
922   EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"TestApp", &value));
923   EXPECT_EQ(L"", value);
924 
925   // .test2 should still be associated with the other app (should not have been
926   // overridden). But it should have our app in its Open With list.
927   ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER,
928                                     L"Software\\Classes\\.test2", KEY_READ));
929   EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"", &value));
930   EXPECT_EQ(L"SomeOtherApp", value);
931   ASSERT_EQ(ERROR_SUCCESS,
932             key.Open(HKEY_CURRENT_USER,
933                      L"Software\\Classes\\.test2\\OpenWithProgids", KEY_READ));
934   EXPECT_TRUE(key.HasValue(L"TestApp"));
935   EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"TestApp", &value));
936   EXPECT_EQ(L"", value);
937 }
938 
TEST_F(ShellUtilRegistryTest,DeleteFileAssociations)939 TEST_F(ShellUtilRegistryTest, DeleteFileAssociations) {
940   // Create file associations.
941   ASSERT_TRUE(ShellUtil::AddFileAssociations(
942       kTestProgid, OpenCommand(), kTestApplicationName, kTestFileTypeName,
943       base::FilePath(kTestIconPath), FileExtensions()));
944 
945   // Delete them.
946   EXPECT_TRUE(ShellUtil::DeleteFileAssociations(kTestProgid));
947 
948   // The class key should have been completely deleted.
949   base::win::RegKey key;
950   std::wstring value;
951   ASSERT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER,
952                                     L"Software\\Classes\\TestApp", KEY_READ));
953 
954   // .test1 and .test2 should no longer be associated with the test app.
955   ASSERT_EQ(ERROR_SUCCESS,
956             key.Open(HKEY_CURRENT_USER,
957                      L"Software\\Classes\\.test1\\OpenWithProgids", KEY_READ));
958   EXPECT_FALSE(key.HasValue(L"TestApp"));
959   ASSERT_EQ(ERROR_SUCCESS,
960             key.Open(HKEY_CURRENT_USER,
961                      L"Software\\Classes\\.test2\\OpenWithProgids", KEY_READ));
962   EXPECT_FALSE(key.HasValue(L"TestApp"));
963 
964   // .test1 should no longer have a default handler.
965   ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER,
966                                     L"Software\\Classes\\.test1", KEY_READ));
967   EXPECT_FALSE(key.HasValue(L""));
968 
969   // .test2 should still have the other app as its default handler.
970   ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER,
971                                     L"Software\\Classes\\.test2", KEY_READ));
972   EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"", &value));
973   EXPECT_EQ(L"SomeOtherApp", value);
974 }
975 
TEST_F(ShellUtilRegistryTest,AddApplicationClass)976 TEST_F(ShellUtilRegistryTest, AddApplicationClass) {
977   // Add TestApp application class and verify registry entries.
978   EXPECT_TRUE(ShellUtil::AddApplicationClass(
979       base::string16(kTestProgid), OpenCommand(), kTestApplicationName,
980       kTestFileTypeName, base::FilePath(kTestIconPath)));
981 
982   base::win::RegKey key;
983   std::wstring value;
984   ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER,
985                                     L"Software\\Classes\\TestApp", KEY_READ));
986   EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"", &value));
987   EXPECT_EQ(L"Test File Type", value);
988   ASSERT_EQ(ERROR_SUCCESS,
989             key.Open(HKEY_CURRENT_USER,
990                      L"Software\\Classes\\TestApp\\DefaultIcon", KEY_READ));
991   EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"", &value));
992   EXPECT_EQ(L"D:\\test.ico,0", value);
993   ASSERT_EQ(
994       ERROR_SUCCESS,
995       key.Open(HKEY_CURRENT_USER,
996                L"Software\\Classes\\TestApp\\shell\\open\\command", KEY_READ));
997   EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"", &value));
998   EXPECT_EQ(L"\"C:\\test.exe\" --single-argument %1", value);
999 
1000   // The Application subkey and values are only required by Windows 8 and later.
1001   if (base::win::GetVersion() >= base::win::Version::WIN8) {
1002     ASSERT_EQ(ERROR_SUCCESS,
1003               key.Open(HKEY_CURRENT_USER,
1004                        L"Software\\Classes\\TestApp\\Application", KEY_READ));
1005     EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"ApplicationName", &value));
1006     EXPECT_EQ(L"Test Application", value);
1007     EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"ApplicationIcon", &value));
1008     EXPECT_EQ(L"D:\\test.ico,0", value);
1009   }
1010 }
1011 
TEST_F(ShellUtilRegistryTest,DeleteApplicationClass)1012 TEST_F(ShellUtilRegistryTest, DeleteApplicationClass) {
1013   ASSERT_TRUE(ShellUtil::AddApplicationClass(
1014       kTestProgid, OpenCommand(), kTestApplicationName, kTestFileTypeName,
1015       base::FilePath(kTestIconPath)));
1016 
1017   base::win::RegKey key;
1018   std::wstring value;
1019   ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER,
1020                                     L"Software\\Classes\\TestApp", KEY_READ));
1021 
1022   EXPECT_TRUE(ShellUtil::DeleteApplicationClass(kTestProgid));
1023   EXPECT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER,
1024                                     L"Software\\Classes\\TestApp", KEY_READ));
1025 }
1026 
TEST_F(ShellUtilRegistryTest,GetFileAssociationsAndAppName)1027 TEST_F(ShellUtilRegistryTest, GetFileAssociationsAndAppName) {
1028   ShellUtil::FileAssociationsAndAppName empty_file_associations_and_app_name(
1029       ShellUtil::GetFileAssociationsAndAppName(kTestProgid));
1030   EXPECT_TRUE(empty_file_associations_and_app_name.app_name.empty());
1031 
1032   // Add file associations and test that GetFileAssociationsAndAppName returns
1033   // the registered file associations and app name. Pass kTestApplicationName
1034   // for the open command, to handle the win7 case, which returns the open
1035   // command executable name as the app_name.
1036   ASSERT_TRUE(ShellUtil::AddFileAssociations(
1037       kTestProgid, OpenCommand(), kTestApplicationName, kTestFileTypeName,
1038       base::FilePath(kTestIconPath), FileExtensions()));
1039   ShellUtil::FileAssociationsAndAppName file_associations_and_app_name(
1040       ShellUtil::GetFileAssociationsAndAppName(kTestProgid));
1041   EXPECT_EQ(file_associations_and_app_name.app_name, kTestApplicationName);
1042   EXPECT_EQ(file_associations_and_app_name.file_associations, FileExtensions());
1043 }
1044 
TEST_F(ShellUtilRegistryTest,GetApplicationForProgId)1045 TEST_F(ShellUtilRegistryTest, GetApplicationForProgId) {
1046   // Create file associations.
1047   ASSERT_TRUE(ShellUtil::AddFileAssociations(
1048       kTestProgid, OpenCommand(), kTestApplicationName, kTestFileTypeName,
1049       base::FilePath(kTestIconPath), FileExtensions()));
1050   base::FilePath exe_path = ShellUtil::GetApplicationPathForProgId(kTestProgid);
1051   EXPECT_EQ(exe_path, base::FilePath(kTestOpenCommand));
1052 }
1053 
TEST(ShellUtilTest,BuildAppModelIdBasic)1054 TEST(ShellUtilTest, BuildAppModelIdBasic) {
1055   std::vector<base::string16> components;
1056   const base::string16 base_app_id(install_static::GetBaseAppId());
1057   components.push_back(base_app_id);
1058   ASSERT_EQ(base_app_id, ShellUtil::BuildAppUserModelId(components));
1059 }
1060 
TEST(ShellUtilTest,BuildAppModelIdManySmall)1061 TEST(ShellUtilTest, BuildAppModelIdManySmall) {
1062   std::vector<base::string16> components;
1063   const base::string16 suffixed_app_id(install_static::GetBaseAppId() +
1064                                        base::string16(L".gab"));
1065   components.push_back(suffixed_app_id);
1066   components.push_back(L"Default");
1067   components.push_back(L"Test");
1068   ASSERT_EQ(suffixed_app_id + L".Default.Test",
1069             ShellUtil::BuildAppUserModelId(components));
1070 }
1071 
TEST(ShellUtilTest,BuildAppModelIdNullTerminatorInTheMiddle)1072 TEST(ShellUtilTest, BuildAppModelIdNullTerminatorInTheMiddle) {
1073   std::vector<base::string16> components;
1074   base::string16 appname_with_nullTerminator(
1075       L"I_have_null_terminator_in_middle");
1076   appname_with_nullTerminator[5] = '\0';
1077   components.push_back(appname_with_nullTerminator);
1078   components.push_back(L"Default");
1079   components.push_back(L"Test");
1080   base::string16 expected_string(L"I_have_nul_in_middle.Default.Test");
1081   expected_string[5] = '\0';
1082   ASSERT_EQ(expected_string, ShellUtil::BuildAppUserModelId(components));
1083 }
1084 
TEST(ShellUtilTest,BuildAppModelIdLongUsernameNormalProfile)1085 TEST(ShellUtilTest, BuildAppModelIdLongUsernameNormalProfile) {
1086   std::vector<base::string16> components;
1087   const base::string16 long_appname(
1088       L"Chrome.a_user_who_has_a_crazy_long_name_with_some_weird@symbols_in_it_"
1089       L"that_goes_over_64_characters");
1090   components.push_back(long_appname);
1091   components.push_back(L"Default");
1092   ASSERT_EQ(L"Chrome.a_user_wer_64_characters.Default",
1093             ShellUtil::BuildAppUserModelId(components));
1094 }
1095 
TEST(ShellUtilTest,BuildAppModelIdLongEverything)1096 TEST(ShellUtilTest, BuildAppModelIdLongEverything) {
1097   std::vector<base::string16> components;
1098   const base::string16 long_appname(
1099       L"Chrome.a_user_who_has_a_crazy_long_name_with_some_weird@symbols_in_it_"
1100       L"that_goes_over_64_characters");
1101   components.push_back(long_appname);
1102   components.push_back(
1103       L"A_crazy_profile_name_not_even_sure_whether_that_is_possible");
1104   const base::string16 constructed_app_id(
1105       ShellUtil::BuildAppUserModelId(components));
1106   ASSERT_LE(constructed_app_id.length(), installer::kMaxAppModelIdLength);
1107   ASSERT_EQ(L"Chrome.a_user_wer_64_characters.A_crazy_profilethat_is_possible",
1108             constructed_app_id);
1109 }
1110 
TEST(ShellUtilTest,GetUserSpecificRegistrySuffix)1111 TEST(ShellUtilTest, GetUserSpecificRegistrySuffix) {
1112   base::string16 suffix;
1113   ASSERT_TRUE(ShellUtil::GetUserSpecificRegistrySuffix(&suffix));
1114   ASSERT_TRUE(base::StartsWith(suffix, L".", base::CompareCase::SENSITIVE));
1115   ASSERT_EQ(27u, suffix.length());
1116   ASSERT_TRUE(base::ContainsOnlyChars(suffix.substr(1),
1117                                       L"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"));
1118 }
1119 
TEST(ShellUtilTest,GetOldUserSpecificRegistrySuffix)1120 TEST(ShellUtilTest, GetOldUserSpecificRegistrySuffix) {
1121   base::string16 suffix;
1122   ASSERT_TRUE(ShellUtil::GetOldUserSpecificRegistrySuffix(&suffix));
1123   ASSERT_TRUE(base::StartsWith(suffix, L".", base::CompareCase::SENSITIVE));
1124 
1125   wchar_t user_name[256];
1126   DWORD size = base::size(user_name);
1127   ASSERT_NE(0, ::GetUserName(user_name, &size));
1128   ASSERT_GE(size, 1U);
1129   ASSERT_STREQ(user_name, suffix.substr(1).c_str());
1130 }
1131