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