Home | History | Annotate | Download | only in test
      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::BUFFER_STANDARD, &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::BUFFER_STANDARD);
    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::ProcessHandle crash_service = NULL;
    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;
    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, 0)) {
    668       base::KillProcess(crash_service, 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 void ScopedVirtualizeHklmAndHkcu::RemoveAllOverrides() {
    683   override_manager_.RemoveAllOverrides();
    684 }
    685 
    686 bool KillProcesses(const std::wstring& executable_name, int exit_code,
    687                    bool wait) {
    688   bool result = true;
    689   base::NamedProcessIterator iter(executable_name, NULL);
    690   while (const base::ProcessEntry* entry = iter.NextProcessEntry()) {
    691     result &= base::KillProcessById(entry->pid(), exit_code, wait);
    692   }
    693   return result;
    694 }
    695 
    696 ScopedChromeFrameRegistrar::RegistrationType GetTestBedType() {
    697   if (GetConfigBool(false, L"PerUserTestBed")) {
    698     return ScopedChromeFrameRegistrar::PER_USER;
    699   } else {
    700     return ScopedChromeFrameRegistrar::SYSTEM_LEVEL;
    701   }
    702 }
    703 
    704 void ClearIESessionHistory() {
    705   base::FilePath session_history_path;
    706   if (!PathService::Get(base::DIR_LOCAL_APP_DATA, &session_history_path))
    707     return;
    708 
    709   session_history_path = session_history_path.AppendASCII("Microsoft");
    710   session_history_path = session_history_path.AppendASCII("Internet Explorer");
    711   session_history_path = session_history_path.AppendASCII("Recovery");
    712   base::DeleteFile(session_history_path, true);
    713 }
    714 
    715 std::string GetLocalIPv4Address() {
    716   std::string address;
    717   net::NetworkInterfaceList nic_list;
    718 
    719   if (!net::GetNetworkList(&nic_list)) {
    720     LOG(ERROR) << "GetNetworkList failed to look up non-loopback adapters. "
    721                << "Tests will be run over the loopback adapter, which may "
    722                << "result in hangs.";
    723   } else {
    724     // GetNetworkList only returns 'Up' non-loopback adapters. Select the first
    725     // IPv4 address found - we should be able to bind/connect over it.
    726     for (size_t i = 0; i < nic_list.size(); ++i) {
    727       if (nic_list[i].address.size() != net::kIPv4AddressSize)
    728         continue;
    729       char* address_string =
    730           inet_ntoa(*reinterpret_cast<in_addr*>(&nic_list[i].address[0]));
    731       DCHECK(address_string != NULL);
    732       if (address_string != NULL) {
    733         LOG(INFO) << "HTTP tests will run over " << address_string << ".";
    734         address.assign(address_string);
    735         break;
    736       }
    737     }
    738   }
    739 
    740   if (address.empty()) {
    741     LOG(ERROR) << "Failed to find a non-loopback IP_V4 address. Tests will be "
    742                << "run over the loopback adapter, which may result in hangs.";
    743     address.assign("127.0.0.1");
    744   }
    745 
    746   return address;
    747 }
    748 
    749 }  // namespace chrome_frame_test
    750