1 // Copyright 2013 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 <string> 6 7 #include <shlobj.h> // Must be before propkey. 8 #include <propkey.h> 9 #include <shellapi.h> 10 11 #include "base/command_line.h" 12 #include "base/strings/string16.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "base/win/scoped_comptr.h" 15 #include "base/win/scoped_propvariant.h" 16 #include "base/win/windows_version.h" 17 #include "chrome/browser/browser_process.h" 18 #include "chrome/browser/extensions/extension_browsertest.h" 19 #include "chrome/browser/profiles/profile.h" 20 #include "chrome/browser/profiles/profile_info_cache.h" 21 #include "chrome/browser/profiles/profile_manager.h" 22 #include "chrome/browser/profiles/profile_shortcut_manager_win.h" 23 #include "chrome/browser/profiles/profiles_state.h" 24 #include "chrome/browser/ui/browser.h" 25 #include "chrome/browser/ui/browser_finder.h" 26 #include "chrome/browser/ui/browser_iterator.h" 27 #include "chrome/browser/ui/browser_window.h" 28 #include "chrome/browser/ui/extensions/application_launch.h" 29 #include "chrome/browser/web_applications/web_app.h" 30 #include "chrome/browser/web_applications/web_app_win.h" 31 #include "chrome/common/chrome_switches.h" 32 #include "chrome/common/extensions/extension_constants.h" 33 #include "chrome/installer/util/browser_distribution.h" 34 #include "chrome/test/base/in_process_browser_test.h" 35 #include "chrome/test/base/test_switches.h" 36 #include "content/public/test/test_utils.h" 37 #include "extensions/common/extension.h" 38 #include "ui/views/win/hwnd_util.h" 39 40 typedef ExtensionBrowserTest BrowserWindowPropertyManagerTest; 41 42 namespace { 43 44 // An observer that resumes test code after a new profile is initialized by 45 // quitting the message loop it's blocked on. 46 void UnblockOnProfileCreation(Profile* profile, 47 Profile::CreateStatus status) { 48 if (status == Profile::CREATE_STATUS_INITIALIZED) 49 base::MessageLoop::current()->Quit(); 50 } 51 52 // Checks that the relaunch name, relaunch command and app icon for the given 53 // |browser| are correct. 54 void ValidateBrowserWindowProperties( 55 const Browser* browser, 56 const base::string16& expected_profile_name) { 57 HWND hwnd = views::HWNDForNativeWindow(browser->window()->GetNativeWindow()); 58 59 base::win::ScopedComPtr<IPropertyStore> pps; 60 HRESULT result = SHGetPropertyStoreForWindow(hwnd, IID_IPropertyStore, 61 pps.ReceiveVoid()); 62 EXPECT_TRUE(SUCCEEDED(result)); 63 64 base::win::ScopedPropVariant prop_var; 65 // The relaunch name should be of the form "Chromium" if there is only 1 66 // profile and "First User - Chromium" if there are more. The expected value 67 // is given by |expected_profile_name|. 68 EXPECT_EQ(S_OK, pps->GetValue(PKEY_AppUserModel_RelaunchDisplayNameResource, 69 prop_var.Receive())); 70 EXPECT_EQ(VT_LPWSTR, prop_var.get().vt); 71 EXPECT_EQ( 72 base::FilePath(profiles::internal::GetShortcutFilenameForProfile( 73 expected_profile_name, 74 BrowserDistribution::GetDistribution())).RemoveExtension().value(), 75 prop_var.get().pwszVal); 76 prop_var.Reset(); 77 78 // The relaunch command should specify the profile. 79 EXPECT_EQ(S_OK, pps->GetValue(PKEY_AppUserModel_RelaunchCommand, 80 prop_var.Receive())); 81 EXPECT_EQ(VT_LPWSTR, prop_var.get().vt); 82 CommandLine cmd_line(CommandLine::FromString(prop_var.get().pwszVal)); 83 EXPECT_EQ(browser->profile()->GetPath().BaseName().value(), 84 cmd_line.GetSwitchValueNative(switches::kProfileDirectory)); 85 prop_var.Reset(); 86 87 // The app icon should be set to the profile icon. 88 EXPECT_EQ(S_OK, pps->GetValue(PKEY_AppUserModel_RelaunchIconResource, 89 prop_var.Receive())); 90 EXPECT_EQ(VT_LPWSTR, prop_var.get().vt); 91 EXPECT_EQ(profiles::internal::GetProfileIconPath( 92 browser->profile()->GetPath()).value(), 93 prop_var.get().pwszVal); 94 prop_var.Reset(); 95 base::MessageLoop::current()->Quit(); 96 } 97 98 void ValidateHostedAppWindowProperties(const Browser* browser, 99 const extensions::Extension* extension) { 100 HWND hwnd = views::HWNDForNativeWindow(browser->window()->GetNativeWindow()); 101 102 base::win::ScopedComPtr<IPropertyStore> pps; 103 HRESULT result = 104 SHGetPropertyStoreForWindow(hwnd, IID_IPropertyStore, pps.ReceiveVoid()); 105 EXPECT_TRUE(SUCCEEDED(result)); 106 107 base::win::ScopedPropVariant prop_var; 108 // The relaunch name should be the extension name. 109 EXPECT_EQ(S_OK, 110 pps->GetValue(PKEY_AppUserModel_RelaunchDisplayNameResource, 111 prop_var.Receive())); 112 EXPECT_EQ(VT_LPWSTR, prop_var.get().vt); 113 EXPECT_EQ(base::UTF8ToWide(extension->name()), prop_var.get().pwszVal); 114 prop_var.Reset(); 115 116 // The relaunch command should specify the profile and the app id. 117 EXPECT_EQ( 118 S_OK, 119 pps->GetValue(PKEY_AppUserModel_RelaunchCommand, prop_var.Receive())); 120 EXPECT_EQ(VT_LPWSTR, prop_var.get().vt); 121 CommandLine cmd_line(CommandLine::FromString(prop_var.get().pwszVal)); 122 EXPECT_EQ(browser->profile()->GetPath().BaseName().value(), 123 cmd_line.GetSwitchValueNative(switches::kProfileDirectory)); 124 EXPECT_EQ(base::UTF8ToWide(extension->id()), 125 cmd_line.GetSwitchValueNative(switches::kAppId)); 126 prop_var.Reset(); 127 128 // The app icon should be set to the extension app icon. 129 base::FilePath web_app_dir = web_app::GetWebAppDataDirectory( 130 browser->profile()->GetPath(), extension->id(), GURL()); 131 EXPECT_EQ(S_OK, 132 pps->GetValue(PKEY_AppUserModel_RelaunchIconResource, 133 prop_var.Receive())); 134 EXPECT_EQ(VT_LPWSTR, prop_var.get().vt); 135 EXPECT_EQ(web_app::internals::GetIconFilePath( 136 web_app_dir, base::UTF8ToUTF16(extension->name())).value(), 137 prop_var.get().pwszVal); 138 prop_var.Reset(); 139 base::MessageLoop::current()->Quit(); 140 } 141 142 void PostValidationTaskToUIThread(const base::Closure& validation_task) { 143 content::BrowserThread::PostTask( 144 content::BrowserThread::UI, FROM_HERE, validation_task); 145 } 146 147 // Posts a validation task to the FILE thread which bounces back to the UI 148 // thread and then does validation. This is necessary because the icon profile 149 // pref only gets set at the end of icon creation (which happens on the FILE 150 // thread) and is set on the UI thread. 151 void WaitAndValidateBrowserWindowProperties( 152 const base::Closure& validation_task) { 153 content::BrowserThread::PostTask( 154 content::BrowserThread::FILE, 155 FROM_HERE, 156 base::Bind(&PostValidationTaskToUIThread, validation_task)); 157 content::RunMessageLoop(); 158 } 159 160 } // namespace 161 162 // Tests that require the profile shortcut manager to be instantiated despite 163 // having --user-data-dir specified. 164 class BrowserTestWithProfileShortcutManager : public InProcessBrowserTest { 165 public: 166 BrowserTestWithProfileShortcutManager() {} 167 168 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 169 command_line->AppendSwitch(switches::kEnableProfileShortcutManager); 170 } 171 172 private: 173 DISALLOW_COPY_AND_ASSIGN(BrowserTestWithProfileShortcutManager); 174 }; 175 176 // Check that the window properties on Windows are properly set. 177 // http://crbug.com/396344 178 IN_PROC_BROWSER_TEST_F(BrowserTestWithProfileShortcutManager, 179 DISABLED_WindowProperties) { 180 // Disable this test in Metro+Ash where Windows window properties aren't used. 181 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) 182 return; 183 184 // This test checks HWND properties that are only available on Win7+. 185 if (base::win::GetVersion() < base::win::VERSION_WIN7) 186 return; 187 188 // Single profile case. The profile name should not be shown. 189 WaitAndValidateBrowserWindowProperties(base::Bind( 190 &ValidateBrowserWindowProperties, browser(), base::string16())); 191 192 // If multiprofile mode is not enabled, we can't test the behavior when there 193 // are multiple profiles. 194 if (!profiles::IsMultipleProfilesEnabled()) 195 return; 196 197 // Two profile case. Both profile names should be shown. 198 ProfileManager* profile_manager = g_browser_process->profile_manager(); 199 ProfileInfoCache& cache = profile_manager->GetProfileInfoCache(); 200 201 base::FilePath path_profile2 = 202 profile_manager->GenerateNextProfileDirectoryPath(); 203 profile_manager->CreateProfileAsync(path_profile2, 204 base::Bind(&UnblockOnProfileCreation), 205 base::string16(), base::string16(), 206 std::string()); 207 208 // Spin to allow profile creation to take place, loop is terminated 209 // by UnblockOnProfileCreation when the profile is created. 210 content::RunMessageLoop(); 211 212 // The default profile's name should be part of the relaunch name. 213 WaitAndValidateBrowserWindowProperties( 214 base::Bind(&ValidateBrowserWindowProperties, 215 browser(), 216 base::UTF8ToUTF16(browser()->profile()->GetProfileName()))); 217 218 // The second profile's name should be part of the relaunch name. 219 Browser* profile2_browser = 220 CreateBrowser(profile_manager->GetProfileByPath(path_profile2)); 221 size_t profile2_index = cache.GetIndexOfProfileWithPath(path_profile2); 222 WaitAndValidateBrowserWindowProperties( 223 base::Bind(&ValidateBrowserWindowProperties, 224 profile2_browser, 225 cache.GetNameOfProfileAtIndex(profile2_index))); 226 } 227 228 // http://crbug.com/396344 229 IN_PROC_BROWSER_TEST_F(BrowserWindowPropertyManagerTest, DISABLED_HostedApp) { 230 #if defined(USE_ASH) 231 // Disable this test in Metro+Ash where Windows window properties aren't used. 232 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) 233 return; 234 #endif 235 236 // This test checks HWND properties that are only available on Win7+. 237 if (base::win::GetVersion() < base::win::VERSION_WIN7) 238 return; 239 240 // Load an app. 241 const extensions::Extension* extension = 242 LoadExtension(test_data_dir_.AppendASCII("app/")); 243 EXPECT_TRUE(extension); 244 245 OpenApplication(AppLaunchParams(browser()->profile(), 246 extension, 247 extensions::LAUNCH_CONTAINER_WINDOW, 248 NEW_FOREGROUND_TAB)); 249 250 // Check that the new browser has an app name. 251 // The launch should have created a new browser. 252 ASSERT_EQ(2u, 253 chrome::GetBrowserCount(browser()->profile(), 254 browser()->host_desktop_type())); 255 256 // Find the new browser. 257 Browser* app_browser = NULL; 258 for (chrome::BrowserIterator it; !it.done() && !app_browser; it.Next()) { 259 if (*it != browser()) 260 app_browser = *it; 261 } 262 ASSERT_TRUE(app_browser); 263 ASSERT_TRUE(app_browser != browser()); 264 265 WaitAndValidateBrowserWindowProperties( 266 base::Bind(&ValidateHostedAppWindowProperties, app_browser, extension)); 267 } 268