Home | History | Annotate | Download | only in browser
      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         base::UTF8ToUTF16(web_app::GenerateApplicationNameFromExtensionId(
     71         base::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