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 string16 url16 = 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 UTF8ToWide(gurl.PathForRequest()); 523 } 524 525 std::wstring GetPathAndQueryFromUrl(const std::wstring& url) { 526 string16 url16 = WideToUTF16(url); 527 GURL gurl = GURL(url16); 528 return UTF8ToWide(gurl.PathForRequest()); 529 } 530 531 std::wstring GetClipboardText() { 532 string16 text16; 533 ui::Clipboard::GetForCurrentThread()->ReadText( 534 ui::CLIPBOARD_TYPE_COPY_PASTE, &text16); 535 return 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(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 LOG(ERROR) << "GetNetworkList failed to look up non-loopback adapters. " 717 << "Tests will be run over the loopback adapter, which may " 718 << "result in hangs."; 719 } else { 720 // GetNetworkList only returns 'Up' non-loopback adapters. Select the first 721 // IPv4 address found - we should be able to bind/connect over it. 722 for (size_t i = 0; i < nic_list.size(); ++i) { 723 if (nic_list[i].address.size() != net::kIPv4AddressSize) 724 continue; 725 char* address_string = 726 inet_ntoa(*reinterpret_cast<in_addr*>(&nic_list[i].address[0])); 727 DCHECK(address_string != NULL); 728 if (address_string != NULL) { 729 LOG(INFO) << "HTTP tests will run over " << address_string << "."; 730 address.assign(address_string); 731 break; 732 } 733 } 734 } 735 736 if (address.empty()) { 737 LOG(ERROR) << "Failed to find a non-loopback IP_V4 address. Tests will be " 738 << "run over the loopback adapter, which may result in hangs."; 739 address.assign("127.0.0.1"); 740 } 741 742 return address; 743 } 744 745 } // namespace chrome_frame_test 746