• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
CloseWindowsThreadCallback(HWND hwnd,LPARAM param)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.
CloseVisibleTopLevelWindowsOnThread(DWORD thread_id)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.
CloseVisibleWindowsOnAllThreads(HANDLE process)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 
GetExecutableAppPath(const std::wstring & file)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 
FormatCommandForApp(const std::wstring & exe_name,const std::wstring & argument)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 
LaunchExecutable(const std::wstring & executable,const std::wstring & argument)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 
LaunchChrome(const std::wstring & url,const base::FilePath & user_data_dir)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 
LaunchIEOnVista(const std::wstring & url)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 
LaunchIE(const std::wstring & url)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 
TakeSnapshotAndLog()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 
CloseAllIEWindows()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 
LowIntegrityToken()310 LowIntegrityToken::LowIntegrityToken() : impersonated_(false) {
311 }
312 
~LowIntegrityToken()313 LowIntegrityToken::~LowIntegrityToken() {
314   RevertToSelf();
315 }
316 
RevertToSelf()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 
Impersonate()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 
IsImpersonated()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 
LaunchIEAsComServer(IWebBrowser2 ** web_browser)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 
GetExeVersion(const std::wstring & exe_path)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 
GetInstalledIEVersion()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 
GetProfilePathForIE()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 
GetTestDataFolder()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 
GetSeleniumTestFolder()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 
GetPathFromUrl(const std::wstring & url)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 
GetPathAndQueryFromUrl(const std::wstring & url)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 
GetClipboardText()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 
DestroyClipboard()538 void DestroyClipboard() {
539   ui::Clipboard::DestroyClipboardForCurrentThread();
540 }
541 
SetClipboardText(const std::wstring & text)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 
AddCFMetaTag(std::string * html_data)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 
~CloseIeAtEndOfScope()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.
DetectRunningCrashService(int timeout_ms)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 
StartCrashService()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 
ScopedVirtualizeHklmAndHkcu()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 
~ScopedVirtualizeHklmAndHkcu()679 ScopedVirtualizeHklmAndHkcu::~ScopedVirtualizeHklmAndHkcu() {
680 }
681 
KillProcesses(const std::wstring & executable_name,int exit_code,bool wait)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 
GetTestBedType()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 
ClearIESessionHistory()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 
GetLocalIPv4Address()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