| OLD | NEW |
| (Empty) |
| 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_frame/test/chrome_frame_test_utils.h" | |
| 6 | |
| 7 #include <atlapp.h> | |
| 8 #include <atlmisc.h> | |
| 9 #include <iepmapi.h> | |
| 10 #include <sddl.h> | |
| 11 #include <shlobj.h> | |
| 12 #include <TlHelp32.h> | |
| 13 #include <winsock2.h> | |
| 14 | |
| 15 #include "base/command_line.h" | |
| 16 #include "base/file_util.h" | |
| 17 #include "base/file_version_info.h" | |
| 18 #include "base/files/file_path.h" | |
| 19 #include "base/memory/scoped_ptr.h" | |
| 20 #include "base/path_service.h" | |
| 21 #include "base/process/kill.h" | |
| 22 #include "base/process/launch.h" | |
| 23 #include "base/process/process.h" | |
| 24 #include "base/process/process_iterator.h" | |
| 25 #include "base/strings/string_number_conversions.h" | |
| 26 #include "base/strings/string_piece.h" | |
| 27 #include "base/strings/string_util.h" | |
| 28 #include "base/strings/stringprintf.h" | |
| 29 #include "base/strings/utf_string_conversions.h" | |
| 30 #include "base/win/registry.h" | |
| 31 #include "base/win/scoped_handle.h" | |
| 32 #include "base/win/windows_version.h" | |
| 33 #include "chrome/common/chrome_paths.h" | |
| 34 #include "chrome/common/chrome_paths_internal.h" | |
| 35 #include "chrome/common/chrome_switches.h" | |
| 36 #include "chrome/test/base/ui_test_utils.h" | |
| 37 #include "chrome_frame/utils.h" | |
| 38 #include "net/base/net_util.h" | |
| 39 #include "testing/gtest/include/gtest/gtest.h" | |
| 40 #include "ui/base/clipboard/clipboard.h" | |
| 41 #include "ui/base/clipboard/scoped_clipboard_writer.h" | |
| 42 | |
| 43 namespace chrome_frame_test { | |
| 44 | |
| 45 const wchar_t kCrashServicePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices"; | |
| 46 | |
| 47 const DWORD kCrashServicePipeDesiredAccess = FILE_READ_DATA | | |
| 48 FILE_WRITE_DATA | | |
| 49 FILE_WRITE_ATTRIBUTES; | |
| 50 | |
| 51 const DWORD kCrashServicePipeFlagsAndAttributes = SECURITY_IDENTIFICATION | | |
| 52 SECURITY_SQOS_PRESENT; | |
| 53 const int kCrashServiceDetectTimeoutMs = 500; | |
| 54 const int kCrashServiceStartupTimeoutMs = 1000; | |
| 55 | |
| 56 const wchar_t kIEImageName[] = L"iexplore.exe"; | |
| 57 const wchar_t kIEBrokerImageName[] = L"ieuser.exe"; | |
| 58 const char kChromeImageName[] = "chrome.exe"; | |
| 59 const wchar_t kIEProfileName[] = L"iexplore"; | |
| 60 const wchar_t kChromeLauncher[] = L"chrome_launcher.exe"; | |
| 61 | |
| 62 #ifndef NDEBUG | |
| 63 const base::TimeDelta kChromeFrameLongNavigationTimeout = | |
| 64 base::TimeDelta::FromSeconds(30); | |
| 65 const base::TimeDelta kChromeFrameVeryLongNavigationTimeout = | |
| 66 base::TimeDelta::FromSeconds(90); | |
| 67 #else | |
| 68 const base::TimeDelta kChromeFrameLongNavigationTimeout = | |
| 69 base::TimeDelta::FromSeconds(10); | |
| 70 const base::TimeDelta kChromeFrameVeryLongNavigationTimeout = | |
| 71 base::TimeDelta::FromSeconds(30); | |
| 72 #endif | |
| 73 | |
| 74 // Callback function for EnumThreadWindows. | |
| 75 BOOL CALLBACK CloseWindowsThreadCallback(HWND hwnd, LPARAM param) { | |
| 76 int& count = *reinterpret_cast<int*>(param); | |
| 77 if (IsWindowVisible(hwnd)) { | |
| 78 if (IsWindowEnabled(hwnd)) { | |
| 79 DWORD results = 0; | |
| 80 if (!::SendMessageTimeout(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0, SMTO_BLOCK, | |
| 81 10000, &results)) { | |
| 82 LOG(WARNING) << "Window hung: " << base::StringPrintf(L"%08X", hwnd); | |
| 83 } | |
| 84 count++; | |
| 85 } else { | |
| 86 LOG(WARNING) << "Skipping disabled window: " | |
| 87 << base::StringPrintf(L"%08X", hwnd); | |
| 88 } | |
| 89 } | |
| 90 return TRUE; // continue enumeration | |
| 91 } | |
| 92 | |
| 93 // Attempts to close all non-child, visible windows on the given thread. | |
| 94 // The return value is the number of visible windows a close request was | |
| 95 // sent to. | |
| 96 int CloseVisibleTopLevelWindowsOnThread(DWORD thread_id) { | |
| 97 int window_close_attempts = 0; | |
| 98 EnumThreadWindows(thread_id, CloseWindowsThreadCallback, | |
| 99 reinterpret_cast<LPARAM>(&window_close_attempts)); | |
| 100 return window_close_attempts; | |
| 101 } | |
| 102 | |
| 103 // Enumerates the threads of a process and attempts to close visible non-child | |
| 104 // windows on all threads of the process. | |
| 105 // The return value is the number of visible windows a close request was | |
| 106 // sent to. | |
| 107 int CloseVisibleWindowsOnAllThreads(HANDLE process) { | |
| 108 DWORD process_id = ::GetProcessId(process); | |
| 109 if (process_id == 0) { | |
| 110 NOTREACHED(); | |
| 111 return 0; | |
| 112 } | |
| 113 | |
| 114 base::win::ScopedHandle snapshot( | |
| 115 CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0)); | |
| 116 if (!snapshot.IsValid()) { | |
| 117 NOTREACHED(); | |
| 118 return 0; | |
| 119 } | |
| 120 | |
| 121 int window_close_attempts = 0; | |
| 122 THREADENTRY32 te = { sizeof(THREADENTRY32) }; | |
| 123 if (Thread32First(snapshot, &te)) { | |
| 124 do { | |
| 125 if (RTL_CONTAINS_FIELD(&te, te.dwSize, th32OwnerProcessID) && | |
| 126 te.th32OwnerProcessID == process_id) { | |
| 127 window_close_attempts += | |
| 128 CloseVisibleTopLevelWindowsOnThread(te.th32ThreadID); | |
| 129 } | |
| 130 te.dwSize = sizeof(te); | |
| 131 } while (Thread32Next(snapshot, &te)); | |
| 132 } | |
| 133 | |
| 134 return window_close_attempts; | |
| 135 } | |
| 136 | |
| 137 std::wstring GetExecutableAppPath(const std::wstring& file) { | |
| 138 std::wstring kAppPathsKey = | |
| 139 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"; | |
| 140 | |
| 141 std::wstring app_path; | |
| 142 base::win::RegKey key(HKEY_LOCAL_MACHINE, (kAppPathsKey + file).c_str(), | |
| 143 KEY_READ); | |
| 144 if (key.Handle()) { | |
| 145 key.ReadValue(NULL, &app_path); | |
| 146 } | |
| 147 | |
| 148 return app_path; | |
| 149 } | |
| 150 | |
| 151 std::wstring FormatCommandForApp(const std::wstring& exe_name, | |
| 152 const std::wstring& argument) { | |
| 153 std::wstring reg_path( | |
| 154 base::StringPrintf(L"Applications\\%ls\\shell\\open\\command", | |
| 155 exe_name.c_str())); | |
| 156 base::win::RegKey key(HKEY_CLASSES_ROOT, reg_path.c_str(), KEY_READ); | |
| 157 | |
| 158 std::wstring command; | |
| 159 if (key.Handle()) { | |
| 160 key.ReadValue(NULL, &command); | |
| 161 int found = command.find(L"%1"); | |
| 162 if (found >= 0) { | |
| 163 command.replace(found, 2, argument); | |
| 164 } | |
| 165 } | |
| 166 return command; | |
| 167 } | |
| 168 | |
| 169 base::ProcessHandle LaunchExecutable(const std::wstring& executable, | |
| 170 const std::wstring& argument) { | |
| 171 base::ProcessHandle process = NULL; | |
| 172 std::wstring path = GetExecutableAppPath(executable); | |
| 173 if (path.empty()) { | |
| 174 path = FormatCommandForApp(executable, argument); | |
| 175 if (path.empty()) { | |
| 176 LOG(ERROR) << "Failed to find executable: " << executable; | |
| 177 } else { | |
| 178 CommandLine cmdline = CommandLine::FromString(path); | |
| 179 if (!base::LaunchProcess(cmdline, base::LaunchOptions(), &process)) { | |
| 180 LOG(ERROR) << "LaunchProcess failed: " << ::GetLastError(); | |
| 181 } | |
| 182 } | |
| 183 } else { | |
| 184 CommandLine cmdline((base::FilePath(path))); | |
| 185 cmdline.AppendArgNative(argument); | |
| 186 if (!base::LaunchProcess(cmdline, base::LaunchOptions(), &process)) { | |
| 187 LOG(ERROR) << "LaunchProcess failed: " << ::GetLastError(); | |
| 188 } | |
| 189 } | |
| 190 return process; | |
| 191 } | |
| 192 | |
| 193 base::ProcessHandle LaunchChrome(const std::wstring& url, | |
| 194 const base::FilePath& user_data_dir) { | |
| 195 base::FilePath path; | |
| 196 PathService::Get(base::DIR_MODULE, &path); | |
| 197 path = path.AppendASCII(kChromeImageName); | |
| 198 | |
| 199 CommandLine cmd(path); | |
| 200 cmd.AppendSwitch(switches::kNoFirstRun); | |
| 201 if (!user_data_dir.empty()) | |
| 202 cmd.AppendSwitchPath(switches::kUserDataDir, user_data_dir); | |
| 203 cmd.AppendArgNative(url); | |
| 204 | |
| 205 base::ProcessHandle process = NULL; | |
| 206 base::LaunchProcess(cmd, base::LaunchOptions(), &process); | |
| 207 return process; | |
| 208 } | |
| 209 | |
| 210 base::ProcessHandle LaunchIEOnVista(const std::wstring& url) { | |
| 211 typedef HRESULT (WINAPI* IELaunchURLPtr)(const wchar_t* url, | |
| 212 PROCESS_INFORMATION* pi, | |
| 213 VOID* info); | |
| 214 | |
| 215 IELaunchURLPtr launch; | |
| 216 PROCESS_INFORMATION pi = {0}; | |
| 217 IELAUNCHURLINFO info = {sizeof info, 0}; | |
| 218 HMODULE h = LoadLibrary(L"ieframe.dll"); | |
| 219 if (!h) { | |
| 220 LOG(ERROR) << "Failed to load ieframe.dll: " << ::GetLastError(); | |
| 221 return NULL; | |
| 222 } | |
| 223 launch = reinterpret_cast<IELaunchURLPtr>(GetProcAddress(h, "IELaunchURL")); | |
| 224 CHECK(launch); | |
| 225 HRESULT hr = launch(url.c_str(), &pi, &info); | |
| 226 FreeLibrary(h); | |
| 227 if (SUCCEEDED(hr)) { | |
| 228 CloseHandle(pi.hThread); | |
| 229 } else { | |
| 230 LOG(ERROR) << base::StringPrintf("IELaunchURL failed: 0x%08X", hr); | |
| 231 } | |
| 232 return pi.hProcess; | |
| 233 } | |
| 234 | |
| 235 base::ProcessHandle LaunchIE(const std::wstring& url) { | |
| 236 if (GetInstalledIEVersion() >= IE_8) { | |
| 237 chrome_frame_test::ClearIESessionHistory(); | |
| 238 } | |
| 239 | |
| 240 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { | |
| 241 return LaunchIEOnVista(url); | |
| 242 } | |
| 243 return LaunchExecutable(kIEImageName, url); | |
| 244 } | |
| 245 | |
| 246 bool TakeSnapshotAndLog() { | |
| 247 testing::UnitTest* unit_test = testing::UnitTest::GetInstance(); | |
| 248 const testing::TestInfo* test_info = unit_test->current_test_info(); | |
| 249 std::string name; | |
| 250 if (test_info != NULL) { | |
| 251 name.append(test_info->test_case_name()) | |
| 252 .append(1, '.') | |
| 253 .append(test_info->name()); | |
| 254 } else { | |
| 255 name = "unknown test"; | |
| 256 } | |
| 257 | |
| 258 base::FilePath snapshot; | |
| 259 if (!ui_test_utils::SaveScreenSnapshotToDesktop(&snapshot)) { | |
| 260 LOG(ERROR) << "Failed saving screen snapshot for " << name; | |
| 261 return false; | |
| 262 } | |
| 263 | |
| 264 LOG(ERROR) << "Saved screen snapshot for " << name << " to " | |
| 265 << snapshot.value(); | |
| 266 return true; | |
| 267 } | |
| 268 | |
| 269 int CloseAllIEWindows() { | |
| 270 int ret = 0; | |
| 271 | |
| 272 base::win::ScopedComPtr<IShellWindows> windows; | |
| 273 HRESULT hr = ::CoCreateInstance(__uuidof(ShellWindows), NULL, CLSCTX_ALL, | |
| 274 IID_IShellWindows, reinterpret_cast<void**>(windows.Receive())); | |
| 275 DCHECK(SUCCEEDED(hr)); | |
| 276 | |
| 277 if (SUCCEEDED(hr)) { | |
| 278 long count = 0; // NOLINT | |
| 279 windows->get_Count(&count); | |
| 280 VARIANT i = { VT_I4 }; | |
| 281 for (i.lVal = 0; i.lVal < count; ++i.lVal) { | |
| 282 base::win::ScopedComPtr<IDispatch> folder; | |
| 283 windows->Item(i, folder.Receive()); | |
| 284 if (folder != NULL) { | |
| 285 base::win::ScopedComPtr<IWebBrowser2> browser; | |
| 286 if (SUCCEEDED(browser.QueryFrom(folder))) { | |
| 287 bool is_ie = true; | |
| 288 HWND window = NULL; | |
| 289 // Check the class of the browser window to make sure we only close | |
| 290 // IE windows. | |
| 291 if (browser->get_HWND(reinterpret_cast<SHANDLE_PTR*>(&window))) { | |
| 292 wchar_t class_name[MAX_PATH]; | |
| 293 if (::GetClassName(window, class_name, arraysize(class_name))) { | |
| 294 is_ie = _wcsicmp(class_name, L"IEFrame") == 0; | |
| 295 } | |
| 296 } | |
| 297 if (is_ie) { | |
| 298 browser->Quit(); | |
| 299 ++ret; | |
| 300 } | |
| 301 } | |
| 302 } | |
| 303 } | |
| 304 } | |
| 305 | |
| 306 return ret; | |
| 307 } | |
| 308 | |
| 309 | |
| 310 LowIntegrityToken::LowIntegrityToken() : impersonated_(false) { | |
| 311 } | |
| 312 | |
| 313 LowIntegrityToken::~LowIntegrityToken() { | |
| 314 RevertToSelf(); | |
| 315 } | |
| 316 | |
| 317 BOOL LowIntegrityToken::RevertToSelf() { | |
| 318 BOOL ok = TRUE; | |
| 319 if (impersonated_) { | |
| 320 DCHECK(IsImpersonated()); | |
| 321 ok = ::RevertToSelf(); | |
| 322 if (ok) | |
| 323 impersonated_ = false; | |
| 324 } | |
| 325 | |
| 326 return ok; | |
| 327 } | |
| 328 | |
| 329 BOOL LowIntegrityToken::Impersonate() { | |
| 330 DCHECK(!impersonated_); | |
| 331 DCHECK(!IsImpersonated()); | |
| 332 HANDLE process_token_handle = NULL; | |
| 333 BOOL ok = ::OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE, | |
| 334 &process_token_handle); | |
| 335 if (!ok) { | |
| 336 LOG(ERROR) << "::OpenProcessToken failed: " << GetLastError(); | |
| 337 return ok; | |
| 338 } | |
| 339 | |
| 340 base::win::ScopedHandle process_token(process_token_handle); | |
| 341 // Create impersonation low integrity token. | |
| 342 HANDLE impersonation_token_handle = NULL; | |
| 343 ok = ::DuplicateTokenEx(process_token, | |
| 344 TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_ADJUST_DEFAULT, NULL, | |
| 345 SecurityImpersonation, TokenImpersonation, &impersonation_token_handle); | |
| 346 if (!ok) { | |
| 347 LOG(ERROR) << "::DuplicateTokenEx failed: " << GetLastError(); | |
| 348 return ok; | |
| 349 } | |
| 350 | |
| 351 // TODO(stoyan): sandbox/win/src/restricted_token_utils.cc has | |
| 352 // SetTokenIntegrityLevel function already. | |
| 353 base::win::ScopedHandle impersonation_token(impersonation_token_handle); | |
| 354 PSID integrity_sid = NULL; | |
| 355 TOKEN_MANDATORY_LABEL tml = {0}; | |
| 356 ok = ::ConvertStringSidToSid(SDDL_ML_LOW, &integrity_sid); | |
| 357 if (!ok) { | |
| 358 LOG(ERROR) << "::ConvertStringSidToSid failed: " << GetLastError(); | |
| 359 return ok; | |
| 360 } | |
| 361 | |
| 362 tml.Label.Attributes = SE_GROUP_INTEGRITY | SE_GROUP_INTEGRITY_ENABLED; | |
| 363 tml.Label.Sid = integrity_sid; | |
| 364 ok = ::SetTokenInformation(impersonation_token, TokenIntegrityLevel, | |
| 365 &tml, sizeof(tml) + ::GetLengthSid(integrity_sid)); | |
| 366 ::LocalFree(integrity_sid); | |
| 367 if (!ok) { | |
| 368 LOG(ERROR) << "::SetTokenInformation failed: " << GetLastError(); | |
| 369 return ok; | |
| 370 } | |
| 371 | |
| 372 // Switch current thread to low integrity. | |
| 373 ok = ::ImpersonateLoggedOnUser(impersonation_token); | |
| 374 if (ok) { | |
| 375 impersonated_ = true; | |
| 376 } else { | |
| 377 LOG(ERROR) << "::ImpersonateLoggedOnUser failed: " << GetLastError(); | |
| 378 } | |
| 379 | |
| 380 return ok; | |
| 381 } | |
| 382 | |
| 383 bool LowIntegrityToken::IsImpersonated() { | |
| 384 HANDLE token = NULL; | |
| 385 if (!::OpenThreadToken(::GetCurrentThread(), 0, false, &token) && | |
| 386 ::GetLastError() != ERROR_NO_TOKEN) { | |
| 387 return true; | |
| 388 } | |
| 389 | |
| 390 if (token) | |
| 391 ::CloseHandle(token); | |
| 392 | |
| 393 return false; | |
| 394 } | |
| 395 | |
| 396 HRESULT LaunchIEAsComServer(IWebBrowser2** web_browser) { | |
| 397 if (!web_browser) | |
| 398 return E_INVALIDARG; | |
| 399 | |
| 400 if (GetInstalledIEVersion() >= IE_8) { | |
| 401 chrome_frame_test::ClearIESessionHistory(); | |
| 402 } | |
| 403 | |
| 404 AllowSetForegroundWindow(ASFW_ANY); | |
| 405 | |
| 406 HRESULT hr = S_OK; | |
| 407 DWORD cocreate_flags = CLSCTX_LOCAL_SERVER; | |
| 408 chrome_frame_test::LowIntegrityToken token; | |
| 409 base::IntegrityLevel integrity_level = base::INTEGRITY_UNKNOWN; | |
| 410 // Vista has a bug which manifests itself when a medium integrity process | |
| 411 // launches a COM server like IE which runs in protected mode due to UAC. | |
| 412 // This causes the IWebBrowser2 interface which is returned to be useless, | |
| 413 // i.e it does not receive any events, etc. Our workaround for this is | |
| 414 // to impersonate a low integrity token and then launch IE. Skip this if the | |
| 415 // tests are running at high integrity, since the workaround results in the | |
| 416 // medium-integrity broker exiting, and the low-integrity IE is therefore | |
| 417 // unable to get chrome_launcher running at medium integrity. | |
| 418 if (base::win::GetVersion() == base::win::VERSION_VISTA && | |
| 419 GetInstalledIEVersion() == IE_7 && | |
| 420 base::GetProcessIntegrityLevel(base::Process::Current().handle(), | |
| 421 &integrity_level) && | |
| 422 integrity_level != base::HIGH_INTEGRITY) { | |
| 423 // Create medium integrity browser that will launch IE broker. | |
| 424 base::win::ScopedComPtr<IWebBrowser2> medium_integrity_browser; | |
| 425 hr = medium_integrity_browser.CreateInstance(CLSID_InternetExplorer, NULL, | |
| 426 CLSCTX_LOCAL_SERVER); | |
| 427 if (FAILED(hr)) | |
| 428 return hr; | |
| 429 medium_integrity_browser->Quit(); | |
| 430 // Broker remains alive. | |
| 431 if (!token.Impersonate()) { | |
| 432 hr = HRESULT_FROM_WIN32(GetLastError()); | |
| 433 return hr; | |
| 434 } | |
| 435 | |
| 436 cocreate_flags |= CLSCTX_ENABLE_CLOAKING; | |
| 437 } | |
| 438 | |
| 439 hr = ::CoCreateInstance(CLSID_InternetExplorer, NULL, | |
| 440 cocreate_flags, IID_IWebBrowser2, | |
| 441 reinterpret_cast<void**>(web_browser)); | |
| 442 // ~LowIntegrityToken() will switch integrity back to medium. | |
| 443 return hr; | |
| 444 } | |
| 445 | |
| 446 std::wstring GetExeVersion(const std::wstring& exe_path) { | |
| 447 scoped_ptr<FileVersionInfo> ie_version_info( | |
| 448 FileVersionInfo::CreateFileVersionInfo(base::FilePath(exe_path))); | |
| 449 return ie_version_info->product_version(); | |
| 450 } | |
| 451 | |
| 452 IEVersion GetInstalledIEVersion() { | |
| 453 std::wstring path(chrome_frame_test::GetExecutableAppPath(kIEImageName)); | |
| 454 std::wstring version(GetExeVersion(path)); | |
| 455 size_t first_dot = version.find(L'.'); | |
| 456 int major_version = 0; | |
| 457 if (!base::StringToInt(base::StringPiece16( | |
| 458 version.data(), | |
| 459 first_dot == std::wstring::npos ? version.size() : first_dot), | |
| 460 &major_version)) { | |
| 461 return IE_UNSUPPORTED; | |
| 462 } | |
| 463 | |
| 464 switch (major_version) { | |
| 465 case 6: | |
| 466 return IE_6; | |
| 467 case 7: | |
| 468 return IE_7; | |
| 469 case 8: | |
| 470 return IE_8; | |
| 471 case 9: | |
| 472 return IE_9; | |
| 473 case 10: | |
| 474 return IE_10; | |
| 475 default: | |
| 476 break; | |
| 477 } | |
| 478 | |
| 479 return IE_UNSUPPORTED; | |
| 480 } | |
| 481 | |
| 482 base::FilePath GetProfilePathForIE() { | |
| 483 base::FilePath profile_path; | |
| 484 // Browsers without IDeleteBrowsingHistory in non-priv mode | |
| 485 // have their profiles moved into "Temporary Internet Files". | |
| 486 // The code below basically retrieves the version of IE and computes | |
| 487 // the profile directory accordingly. | |
| 488 if (GetInstalledIEVersion() <= IE_7) { | |
| 489 profile_path = GetIETemporaryFilesFolder(); | |
| 490 profile_path = profile_path.Append(L"Google Chrome Frame"); | |
| 491 } else { | |
| 492 GetChromeFrameProfilePath(kIEProfileName, &profile_path); | |
| 493 } | |
| 494 return profile_path; | |
| 495 } | |
| 496 | |
| 497 base::FilePath GetTestDataFolder() { | |
| 498 base::FilePath test_dir; | |
| 499 PathService::Get(base::DIR_SOURCE_ROOT, &test_dir); | |
| 500 test_dir = test_dir.Append(FILE_PATH_LITERAL("chrome_frame")) | |
| 501 .Append(FILE_PATH_LITERAL("test")) | |
| 502 .Append(FILE_PATH_LITERAL("data")); | |
| 503 return test_dir; | |
| 504 } | |
| 505 | |
| 506 base::FilePath GetSeleniumTestFolder() { | |
| 507 base::FilePath test_dir; | |
| 508 PathService::Get(base::DIR_SOURCE_ROOT, &test_dir); | |
| 509 test_dir = test_dir.Append(FILE_PATH_LITERAL("data")) | |
| 510 .Append(FILE_PATH_LITERAL("selenium_core")); | |
| 511 return test_dir; | |
| 512 } | |
| 513 | |
| 514 std::wstring GetPathFromUrl(const std::wstring& url) { | |
| 515 base::string16 url16 = base::WideToUTF16(url); | |
| 516 GURL gurl = GURL(url16); | |
| 517 if (gurl.has_query()) { | |
| 518 GURL::Replacements replacements; | |
| 519 replacements.ClearQuery(); | |
| 520 gurl = gurl.ReplaceComponents(replacements); | |
| 521 } | |
| 522 return base::UTF8ToWide(gurl.PathForRequest()); | |
| 523 } | |
| 524 | |
| 525 std::wstring GetPathAndQueryFromUrl(const std::wstring& url) { | |
| 526 base::string16 url16 = base::WideToUTF16(url); | |
| 527 GURL gurl = GURL(url16); | |
| 528 return base::UTF8ToWide(gurl.PathForRequest()); | |
| 529 } | |
| 530 | |
| 531 std::wstring GetClipboardText() { | |
| 532 base::string16 text16; | |
| 533 ui::Clipboard::GetForCurrentThread()->ReadText( | |
| 534 ui::CLIPBOARD_TYPE_COPY_PASTE, &text16); | |
| 535 return base::UTF16ToWide(text16); | |
| 536 } | |
| 537 | |
| 538 void DestroyClipboard() { | |
| 539 ui::Clipboard::DestroyClipboardForCurrentThread(); | |
| 540 } | |
| 541 | |
| 542 void SetClipboardText(const std::wstring& text) { | |
| 543 ui::ScopedClipboardWriter clipboard_writer( | |
| 544 ui::Clipboard::GetForCurrentThread(), | |
| 545 ui::CLIPBOARD_TYPE_COPY_PASTE); | |
| 546 clipboard_writer.WriteText(base::WideToUTF16(text)); | |
| 547 } | |
| 548 | |
| 549 bool AddCFMetaTag(std::string* html_data) { | |
| 550 if (!html_data) { | |
| 551 NOTREACHED(); | |
| 552 return false; | |
| 553 } | |
| 554 std::string lower = StringToLowerASCII(*html_data); | |
| 555 size_t head = lower.find("<head>"); | |
| 556 if (head == std::string::npos) { | |
| 557 // Add missing head section. | |
| 558 size_t html = lower.find("<html>"); | |
| 559 if (html != std::string::npos) { | |
| 560 head = html + strlen("<html>"); | |
| 561 html_data->insert(head, "<head></head>"); | |
| 562 } else { | |
| 563 LOG(ERROR) << "Meta tag will not be injected " | |
| 564 << "because the html tag could not be found"; | |
| 565 } | |
| 566 } | |
| 567 if (head != std::string::npos) { | |
| 568 html_data->insert( | |
| 569 head + strlen("<head>"), | |
| 570 "<meta http-equiv=\"x-ua-compatible\" content=\"chrome=1\" />"); | |
| 571 } | |
| 572 return head != std::string::npos; | |
| 573 } | |
| 574 | |
| 575 CloseIeAtEndOfScope::~CloseIeAtEndOfScope() { | |
| 576 int closed = CloseAllIEWindows(); | |
| 577 LOG_IF(ERROR, closed != 0) << "Closed " << closed << " windows forcefully"; | |
| 578 } | |
| 579 | |
| 580 // Attempt to connect to a running crash_service instance. Success occurs if we | |
| 581 // can actually connect to the service's pipe or we receive ERROR_PIPE_BUSY. | |
| 582 // Waits up to |timeout_ms| for success. |timeout_ms| may be 0, meaning only try | |
| 583 // once, or negative, meaning wait forever. | |
| 584 bool DetectRunningCrashService(int timeout_ms) { | |
| 585 // Wait for the crash_service.exe to be ready for clients. | |
| 586 base::Time start = base::Time::Now(); | |
| 587 base::win::ScopedHandle new_pipe; | |
| 588 | |
| 589 while (true) { | |
| 590 new_pipe.Set(::CreateFile(kCrashServicePipeName, | |
| 591 kCrashServicePipeDesiredAccess, | |
| 592 0, // dwShareMode | |
| 593 NULL, // lpSecurityAttributes | |
| 594 OPEN_EXISTING, | |
| 595 kCrashServicePipeFlagsAndAttributes, | |
| 596 NULL)); // hTemplateFile | |
| 597 | |
| 598 if (new_pipe.IsValid()) { | |
| 599 return true; | |
| 600 } | |
| 601 | |
| 602 switch (::GetLastError()) { | |
| 603 case ERROR_PIPE_BUSY: | |
| 604 // OK, it exists, let's assume that clients will eventually be able to | |
| 605 // connect to it. | |
| 606 return true; | |
| 607 case ERROR_FILE_NOT_FOUND: | |
| 608 // Wait a bit longer | |
| 609 break; | |
| 610 default: | |
| 611 DPLOG(WARNING) << "Unexpected error while checking crash_service.exe's " | |
| 612 << "pipe."; | |
| 613 // Go ahead and wait in case it clears up. | |
| 614 break; | |
| 615 } | |
| 616 | |
| 617 if (timeout_ms == 0) { | |
| 618 return false; | |
| 619 } else if (timeout_ms > 0) { | |
| 620 base::TimeDelta duration = base::Time::Now() - start; | |
| 621 if (duration.InMilliseconds() > timeout_ms) { | |
| 622 return false; | |
| 623 } | |
| 624 } | |
| 625 | |
| 626 Sleep(10); | |
| 627 } | |
| 628 } | |
| 629 | |
| 630 base::ProcessHandle StartCrashService() { | |
| 631 if (DetectRunningCrashService(kCrashServiceDetectTimeoutMs)) { | |
| 632 VLOG(1) << "crash_service.exe is already running. We will use the " | |
| 633 "existing process and leave it running after tests complete."; | |
| 634 return NULL; | |
| 635 } | |
| 636 | |
| 637 base::FilePath exe_dir; | |
| 638 if (!PathService::Get(base::DIR_EXE, &exe_dir)) { | |
| 639 DCHECK(false); | |
| 640 return NULL; | |
| 641 } | |
| 642 | |
| 643 base::win::ScopedHandle crash_service; | |
| 644 | |
| 645 VLOG(1) << "Starting crash_service.exe so you know if a test crashes!"; | |
| 646 | |
| 647 base::FilePath crash_service_path = exe_dir.AppendASCII("crash_service.exe"); | |
| 648 if (!base::LaunchProcess(crash_service_path.value(), base::LaunchOptions(), | |
| 649 &crash_service)) { | |
| 650 LOG(ERROR) << "Couldn't start crash_service.exe"; | |
| 651 return NULL; | |
| 652 } | |
| 653 | |
| 654 base::Time start = base::Time::Now(); | |
| 655 | |
| 656 if (DetectRunningCrashService(kCrashServiceStartupTimeoutMs)) { | |
| 657 VLOG(1) << "crash_service.exe is ready for clients in " | |
| 658 << (base::Time::Now() - start).InMilliseconds() << " ms."; | |
| 659 return crash_service.Take(); | |
| 660 } else { | |
| 661 LOG(ERROR) << "crash_service.exe failed to accept client connections " | |
| 662 "within " << kCrashServiceStartupTimeoutMs << " ms. " | |
| 663 "Terminating it now."; | |
| 664 | |
| 665 // First check to see if it's even still running just to minimize the | |
| 666 // likelihood of spurious error messages from KillProcess. | |
| 667 if (WAIT_OBJECT_0 != ::WaitForSingleObject(crash_service.Get(), 0)) { | |
| 668 base::KillProcess(crash_service.Get(), 0, false); | |
| 669 } | |
| 670 return NULL; | |
| 671 } | |
| 672 } | |
| 673 | |
| 674 ScopedVirtualizeHklmAndHkcu::ScopedVirtualizeHklmAndHkcu() { | |
| 675 override_manager_.OverrideRegistry(HKEY_LOCAL_MACHINE, L"hklm_fake"); | |
| 676 override_manager_.OverrideRegistry(HKEY_CURRENT_USER, L"hkcu_fake"); | |
| 677 } | |
| 678 | |
| 679 ScopedVirtualizeHklmAndHkcu::~ScopedVirtualizeHklmAndHkcu() { | |
| 680 } | |
| 681 | |
| 682 bool KillProcesses(const std::wstring& executable_name, int exit_code, | |
| 683 bool wait) { | |
| 684 bool result = true; | |
| 685 base::NamedProcessIterator iter(executable_name, NULL); | |
| 686 while (const base::ProcessEntry* entry = iter.NextProcessEntry()) { | |
| 687 result &= base::KillProcessById(entry->pid(), exit_code, wait); | |
| 688 } | |
| 689 return result; | |
| 690 } | |
| 691 | |
| 692 ScopedChromeFrameRegistrar::RegistrationType GetTestBedType() { | |
| 693 if (GetConfigBool(false, L"PerUserTestBed")) { | |
| 694 return ScopedChromeFrameRegistrar::PER_USER; | |
| 695 } else { | |
| 696 return ScopedChromeFrameRegistrar::SYSTEM_LEVEL; | |
| 697 } | |
| 698 } | |
| 699 | |
| 700 void ClearIESessionHistory() { | |
| 701 base::FilePath session_history_path; | |
| 702 if (!PathService::Get(base::DIR_LOCAL_APP_DATA, &session_history_path)) | |
| 703 return; | |
| 704 | |
| 705 session_history_path = session_history_path.AppendASCII("Microsoft"); | |
| 706 session_history_path = session_history_path.AppendASCII("Internet Explorer"); | |
| 707 session_history_path = session_history_path.AppendASCII("Recovery"); | |
| 708 base::DeleteFile(session_history_path, true); | |
| 709 } | |
| 710 | |
| 711 std::string GetLocalIPv4Address() { | |
| 712 std::string address; | |
| 713 net::NetworkInterfaceList nic_list; | |
| 714 | |
| 715 if (!net::GetNetworkList(&nic_list, | |
| 716 net::INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES)) { | |
| 717 LOG(ERROR) << "GetNetworkList failed to look up non-loopback adapters. " | |
| 718 << "Tests will be run over the loopback adapter, which may " | |
| 719 << "result in hangs."; | |
| 720 } else { | |
| 721 // GetNetworkList only returns 'Up' non-loopback adapters. Select the first | |
| 722 // IPv4 address found - we should be able to bind/connect over it. | |
| 723 for (size_t i = 0; i < nic_list.size(); ++i) { | |
| 724 if (nic_list[i].address.size() != net::kIPv4AddressSize) | |
| 725 continue; | |
| 726 char* address_string = | |
| 727 inet_ntoa(*reinterpret_cast<in_addr*>(&nic_list[i].address[0])); | |
| 728 DCHECK(address_string != NULL); | |
| 729 if (address_string != NULL) { | |
| 730 LOG(INFO) << "HTTP tests will run over " << address_string << "."; | |
| 731 address.assign(address_string); | |
| 732 break; | |
| 733 } | |
| 734 } | |
| 735 } | |
| 736 | |
| 737 if (address.empty()) { | |
| 738 LOG(ERROR) << "Failed to find a non-loopback IP_V4 address. Tests will be " | |
| 739 << "run over the loopback adapter, which may result in hangs."; | |
| 740 address.assign("127.0.0.1"); | |
| 741 } | |
| 742 | |
| 743 return address; | |
| 744 } | |
| 745 | |
| 746 } // namespace chrome_frame_test | |
| OLD | NEW |