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 40typedef ExtensionBrowserTest BrowserWindowPropertyManagerTest; 41 42namespace { 43 44// An observer that resumes test code after a new profile is initialized by 45// quitting the message loop it's blocked on. 46void 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. 54void 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 98void 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 142void 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. 151void 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. 164class 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 178IN_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 229IN_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