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.
5 #include "chrome_frame/test/chrome_frame_test_utils.h"
14 #include "base/command_line.h"
15 #include "base/file_util.h"
16 #include "base/file_version_info.h"
17 #include "base/files/file_path.h"
18 #include "base/memory/scoped_ptr.h"
19 #include "base/path_service.h"
20 #include "base/process.h"
21 #include "base/process_util.h"
22 #include "base/string_number_conversions.h"
23 #include "base/string_piece.h"
24 #include "base/string_util.h"
25 #include "base/stringprintf.h"
26 #include "base/utf_string_conversions.h"
27 #include "base/win/registry.h"
28 #include "base/win/scoped_handle.h"
29 #include "base/win/windows_version.h"
30 #include "chrome/common/chrome_paths.h"
31 #include "chrome/common/chrome_paths_internal.h"
32 #include "chrome/common/chrome_switches.h"
33 #include "chrome/test/base/ui_test_utils.h"
34 #include "chrome_frame/utils.h"
35 #include "net/base/net_util.h"
36 #include "testing/gtest/include/gtest/gtest.h"
37 #include "ui/base/clipboard/clipboard.h"
38 #include "ui/base/clipboard/scoped_clipboard_writer.h"
40 namespace chrome_frame_test
{
42 const wchar_t kCrashServicePipeName
[] = L
"\\\\.\\pipe\\ChromeCrashServices";
44 const DWORD kCrashServicePipeDesiredAccess
= FILE_READ_DATA
|
46 FILE_WRITE_ATTRIBUTES
;
48 const DWORD kCrashServicePipeFlagsAndAttributes
= SECURITY_IDENTIFICATION
|
49 SECURITY_SQOS_PRESENT
;
50 const int kCrashServiceDetectTimeoutMs
= 500;
51 const int kCrashServiceStartupTimeoutMs
= 1000;
53 const wchar_t kIEImageName
[] = L
"iexplore.exe";
54 const wchar_t kIEBrokerImageName
[] = L
"ieuser.exe";
55 const char kChromeImageName
[] = "chrome.exe";
56 const wchar_t kIEProfileName
[] = L
"iexplore";
57 const wchar_t kChromeLauncher
[] = L
"chrome_launcher.exe";
60 const base::TimeDelta kChromeFrameLongNavigationTimeout
=
61 base::TimeDelta::FromSeconds(30);
62 const base::TimeDelta kChromeFrameVeryLongNavigationTimeout
=
63 base::TimeDelta::FromSeconds(90);
65 const base::TimeDelta kChromeFrameLongNavigationTimeout
=
66 base::TimeDelta::FromSeconds(10);
67 const base::TimeDelta kChromeFrameVeryLongNavigationTimeout
=
68 base::TimeDelta::FromSeconds(30);
71 // Callback function for EnumThreadWindows.
72 BOOL CALLBACK
CloseWindowsThreadCallback(HWND hwnd
, LPARAM param
) {
73 int& count
= *reinterpret_cast<int*>(param
);
74 if (IsWindowVisible(hwnd
)) {
75 if (IsWindowEnabled(hwnd
)) {
77 if (!::SendMessageTimeout(hwnd
, WM_SYSCOMMAND
, SC_CLOSE
, 0, SMTO_BLOCK
,
79 LOG(WARNING
) << "Window hung: " << base::StringPrintf(L
"%08X", hwnd
);
83 LOG(WARNING
) << "Skipping disabled window: "
84 << base::StringPrintf(L
"%08X", hwnd
);
87 return TRUE
; // continue enumeration
90 // Attempts to close all non-child, visible windows on the given thread.
91 // The return value is the number of visible windows a close request was
93 int CloseVisibleTopLevelWindowsOnThread(DWORD thread_id
) {
94 int window_close_attempts
= 0;
95 EnumThreadWindows(thread_id
, CloseWindowsThreadCallback
,
96 reinterpret_cast<LPARAM
>(&window_close_attempts
));
97 return window_close_attempts
;
100 // Enumerates the threads of a process and attempts to close visible non-child
101 // windows on all threads of the process.
102 // The return value is the number of visible windows a close request was
104 int CloseVisibleWindowsOnAllThreads(HANDLE process
) {
105 DWORD process_id
= ::GetProcessId(process
);
106 if (process_id
== 0) {
111 base::win::ScopedHandle
snapshot(
112 CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD
, 0));
113 if (!snapshot
.IsValid()) {
118 int window_close_attempts
= 0;
119 THREADENTRY32 te
= { sizeof(THREADENTRY32
) };
120 if (Thread32First(snapshot
, &te
)) {
122 if (RTL_CONTAINS_FIELD(&te
, te
.dwSize
, th32OwnerProcessID
) &&
123 te
.th32OwnerProcessID
== process_id
) {
124 window_close_attempts
+=
125 CloseVisibleTopLevelWindowsOnThread(te
.th32ThreadID
);
127 te
.dwSize
= sizeof(te
);
128 } while (Thread32Next(snapshot
, &te
));
131 return window_close_attempts
;
134 std::wstring
GetExecutableAppPath(const std::wstring
& file
) {
135 std::wstring kAppPathsKey
=
136 L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\";
138 std::wstring app_path
;
139 base::win::RegKey
key(HKEY_LOCAL_MACHINE
, (kAppPathsKey
+ file
).c_str(),
142 key
.ReadValue(NULL
, &app_path
);
148 std::wstring
FormatCommandForApp(const std::wstring
& exe_name
,
149 const std::wstring
& argument
) {
150 std::wstring
reg_path(
151 base::StringPrintf(L
"Applications\\%ls\\shell\\open\\command",
153 base::win::RegKey
key(HKEY_CLASSES_ROOT
, reg_path
.c_str(), KEY_READ
);
155 std::wstring command
;
157 key
.ReadValue(NULL
, &command
);
158 int found
= command
.find(L
"%1");
160 command
.replace(found
, 2, argument
);
166 base::ProcessHandle
LaunchExecutable(const std::wstring
& executable
,
167 const std::wstring
& argument
) {
168 base::ProcessHandle process
= NULL
;
169 std::wstring path
= GetExecutableAppPath(executable
);
171 path
= FormatCommandForApp(executable
, argument
);
173 LOG(ERROR
) << "Failed to find executable: " << executable
;
175 CommandLine cmdline
= CommandLine::FromString(path
);
176 if (!base::LaunchProcess(cmdline
, base::LaunchOptions(), &process
)) {
177 LOG(ERROR
) << "LaunchProcess failed: " << ::GetLastError();
181 CommandLine
cmdline((base::FilePath(path
)));
182 cmdline
.AppendArgNative(argument
);
183 if (!base::LaunchProcess(cmdline
, base::LaunchOptions(), &process
)) {
184 LOG(ERROR
) << "LaunchProcess failed: " << ::GetLastError();
190 base::ProcessHandle
LaunchChrome(const std::wstring
& url
,
191 const base::FilePath
& user_data_dir
) {
193 PathService::Get(base::DIR_MODULE
, &path
);
194 path
= path
.AppendASCII(kChromeImageName
);
196 CommandLine
cmd(path
);
197 cmd
.AppendSwitch(switches::kNoFirstRun
);
198 if (!user_data_dir
.empty())
199 cmd
.AppendSwitchPath(switches::kUserDataDir
, user_data_dir
);
200 cmd
.AppendArgNative(url
);
202 base::ProcessHandle process
= NULL
;
203 base::LaunchProcess(cmd
, base::LaunchOptions(), &process
);
207 base::ProcessHandle
LaunchIEOnVista(const std::wstring
& url
) {
208 typedef HRESULT (WINAPI
* IELaunchURLPtr
)(const wchar_t* url
,
209 PROCESS_INFORMATION
* pi
,
212 IELaunchURLPtr launch
;
213 PROCESS_INFORMATION pi
= {0};
214 IELAUNCHURLINFO info
= {sizeof info
, 0};
215 HMODULE h
= LoadLibrary(L
"ieframe.dll");
217 LOG(ERROR
) << "Failed to load ieframe.dll: " << ::GetLastError();
220 launch
= reinterpret_cast<IELaunchURLPtr
>(GetProcAddress(h
, "IELaunchURL"));
222 HRESULT hr
= launch(url
.c_str(), &pi
, &info
);
225 CloseHandle(pi
.hThread
);
227 LOG(ERROR
) << base::StringPrintf("IELaunchURL failed: 0x%08X", hr
);
232 base::ProcessHandle
LaunchIE(const std::wstring
& url
) {
233 if (GetInstalledIEVersion() >= IE_8
) {
234 chrome_frame_test::ClearIESessionHistory();
237 if (base::win::GetVersion() >= base::win::VERSION_VISTA
) {
238 return LaunchIEOnVista(url
);
240 return LaunchExecutable(kIEImageName
, url
);
243 bool TakeSnapshotAndLog() {
244 testing::UnitTest
* unit_test
= testing::UnitTest::GetInstance();
245 const testing::TestInfo
* test_info
= unit_test
->current_test_info();
247 if (test_info
!= NULL
) {
248 name
.append(test_info
->test_case_name())
250 .append(test_info
->name());
252 name
= "unknown test";
255 base::FilePath snapshot
;
256 if (!ui_test_utils::SaveScreenSnapshotToDesktop(&snapshot
)) {
257 LOG(ERROR
) << "Failed saving screen snapshot for " << name
;
261 LOG(ERROR
) << "Saved screen snapshot for " << name
<< " to "
266 int CloseAllIEWindows() {
269 base::win::ScopedComPtr
<IShellWindows
> windows
;
270 HRESULT hr
= ::CoCreateInstance(__uuidof(ShellWindows
), NULL
, CLSCTX_ALL
,
271 IID_IShellWindows
, reinterpret_cast<void**>(windows
.Receive()));
272 DCHECK(SUCCEEDED(hr
));
275 long count
= 0; // NOLINT
276 windows
->get_Count(&count
);
277 VARIANT i
= { VT_I4
};
278 for (i
.lVal
= 0; i
.lVal
< count
; ++i
.lVal
) {
279 base::win::ScopedComPtr
<IDispatch
> folder
;
280 windows
->Item(i
, folder
.Receive());
281 if (folder
!= NULL
) {
282 base::win::ScopedComPtr
<IWebBrowser2
> browser
;
283 if (SUCCEEDED(browser
.QueryFrom(folder
))) {
286 // Check the class of the browser window to make sure we only close
288 if (browser
->get_HWND(reinterpret_cast<SHANDLE_PTR
*>(&window
))) {
289 wchar_t class_name
[MAX_PATH
];
290 if (::GetClassName(window
, class_name
, arraysize(class_name
))) {
291 is_ie
= _wcsicmp(class_name
, L
"IEFrame") == 0;
307 LowIntegrityToken::LowIntegrityToken() : impersonated_(false) {
310 LowIntegrityToken::~LowIntegrityToken() {
314 BOOL
LowIntegrityToken::RevertToSelf() {
317 DCHECK(IsImpersonated());
318 ok
= ::RevertToSelf();
320 impersonated_
= false;
326 BOOL
LowIntegrityToken::Impersonate() {
327 DCHECK(!impersonated_
);
328 DCHECK(!IsImpersonated());
329 HANDLE process_token_handle
= NULL
;
330 BOOL ok
= ::OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE
,
331 &process_token_handle
);
333 LOG(ERROR
) << "::OpenProcessToken failed: " << GetLastError();
337 base::win::ScopedHandle
process_token(process_token_handle
);
338 // Create impersonation low integrity token.
339 HANDLE impersonation_token_handle
= NULL
;
340 ok
= ::DuplicateTokenEx(process_token
,
341 TOKEN_QUERY
| TOKEN_IMPERSONATE
| TOKEN_ADJUST_DEFAULT
, NULL
,
342 SecurityImpersonation
, TokenImpersonation
, &impersonation_token_handle
);
344 LOG(ERROR
) << "::DuplicateTokenEx failed: " << GetLastError();
348 // TODO(stoyan): sandbox/win/src/restricted_token_utils.cc has
349 // SetTokenIntegrityLevel function already.
350 base::win::ScopedHandle
impersonation_token(impersonation_token_handle
);
351 PSID integrity_sid
= NULL
;
352 TOKEN_MANDATORY_LABEL tml
= {0};
353 ok
= ::ConvertStringSidToSid(SDDL_ML_LOW
, &integrity_sid
);
355 LOG(ERROR
) << "::ConvertStringSidToSid failed: " << GetLastError();
359 tml
.Label
.Attributes
= SE_GROUP_INTEGRITY
| SE_GROUP_INTEGRITY_ENABLED
;
360 tml
.Label
.Sid
= integrity_sid
;
361 ok
= ::SetTokenInformation(impersonation_token
, TokenIntegrityLevel
,
362 &tml
, sizeof(tml
) + ::GetLengthSid(integrity_sid
));
363 ::LocalFree(integrity_sid
);
365 LOG(ERROR
) << "::SetTokenInformation failed: " << GetLastError();
369 // Switch current thread to low integrity.
370 ok
= ::ImpersonateLoggedOnUser(impersonation_token
);
372 impersonated_
= true;
374 LOG(ERROR
) << "::ImpersonateLoggedOnUser failed: " << GetLastError();
380 bool LowIntegrityToken::IsImpersonated() {
382 if (!::OpenThreadToken(::GetCurrentThread(), 0, false, &token
) &&
383 ::GetLastError() != ERROR_NO_TOKEN
) {
388 ::CloseHandle(token
);
393 HRESULT
LaunchIEAsComServer(IWebBrowser2
** web_browser
) {
397 if (GetInstalledIEVersion() >= IE_8
) {
398 chrome_frame_test::ClearIESessionHistory();
401 AllowSetForegroundWindow(ASFW_ANY
);
404 DWORD cocreate_flags
= CLSCTX_LOCAL_SERVER
;
405 chrome_frame_test::LowIntegrityToken token
;
406 base::IntegrityLevel integrity_level
= base::INTEGRITY_UNKNOWN
;
407 // Vista has a bug which manifests itself when a medium integrity process
408 // launches a COM server like IE which runs in protected mode due to UAC.
409 // This causes the IWebBrowser2 interface which is returned to be useless,
410 // i.e it does not receive any events, etc. Our workaround for this is
411 // to impersonate a low integrity token and then launch IE. Skip this if the
412 // tests are running at high integrity, since the workaround results in the
413 // medium-integrity broker exiting, and the low-integrity IE is therefore
414 // unable to get chrome_launcher running at medium integrity.
415 if (base::win::GetVersion() == base::win::VERSION_VISTA
&&
416 GetInstalledIEVersion() == IE_7
&&
417 base::GetProcessIntegrityLevel(base::Process::Current().handle(),
419 integrity_level
!= base::HIGH_INTEGRITY
) {
420 // Create medium integrity browser that will launch IE broker.
421 base::win::ScopedComPtr
<IWebBrowser2
> medium_integrity_browser
;
422 hr
= medium_integrity_browser
.CreateInstance(CLSID_InternetExplorer
, NULL
,
423 CLSCTX_LOCAL_SERVER
);
426 medium_integrity_browser
->Quit();
427 // Broker remains alive.
428 if (!token
.Impersonate()) {
429 hr
= HRESULT_FROM_WIN32(GetLastError());
433 cocreate_flags
|= CLSCTX_ENABLE_CLOAKING
;
436 hr
= ::CoCreateInstance(CLSID_InternetExplorer
, NULL
,
437 cocreate_flags
, IID_IWebBrowser2
,
438 reinterpret_cast<void**>(web_browser
));
439 // ~LowIntegrityToken() will switch integrity back to medium.
443 std::wstring
GetExeVersion(const std::wstring
& exe_path
) {
444 scoped_ptr
<FileVersionInfo
> ie_version_info(
445 FileVersionInfo::CreateFileVersionInfo(base::FilePath(exe_path
)));
446 return ie_version_info
->product_version();
449 IEVersion
GetInstalledIEVersion() {
450 std::wstring
path(chrome_frame_test::GetExecutableAppPath(kIEImageName
));
451 std::wstring
version(GetExeVersion(path
));
452 size_t first_dot
= version
.find(L
'.');
453 int major_version
= 0;
454 if (!base::StringToInt(base::StringPiece16(
456 first_dot
== std::wstring::npos
? version
.size() : first_dot
),
458 return IE_UNSUPPORTED
;
461 switch (major_version
) {
476 return IE_UNSUPPORTED
;
479 base::FilePath
GetProfilePathForIE() {
480 base::FilePath profile_path
;
481 // Browsers without IDeleteBrowsingHistory in non-priv mode
482 // have their profiles moved into "Temporary Internet Files".
483 // The code below basically retrieves the version of IE and computes
484 // the profile directory accordingly.
485 if (GetInstalledIEVersion() <= IE_7
) {
486 profile_path
= GetIETemporaryFilesFolder();
487 profile_path
= profile_path
.Append(L
"Google Chrome Frame");
489 GetChromeFrameProfilePath(kIEProfileName
, &profile_path
);
494 base::FilePath
GetTestDataFolder() {
495 base::FilePath test_dir
;
496 PathService::Get(base::DIR_SOURCE_ROOT
, &test_dir
);
497 test_dir
= test_dir
.Append(FILE_PATH_LITERAL("chrome_frame"))
498 .Append(FILE_PATH_LITERAL("test"))
499 .Append(FILE_PATH_LITERAL("data"));
503 base::FilePath
GetSeleniumTestFolder() {
504 base::FilePath test_dir
;
505 PathService::Get(base::DIR_SOURCE_ROOT
, &test_dir
);
506 test_dir
= test_dir
.Append(FILE_PATH_LITERAL("data"))
507 .Append(FILE_PATH_LITERAL("selenium_core"));
511 std::wstring
GetPathFromUrl(const std::wstring
& url
) {
512 string16 url16
= WideToUTF16(url
);
513 GURL gurl
= GURL(url16
);
514 if (gurl
.has_query()) {
515 GURL::Replacements replacements
;
516 replacements
.ClearQuery();
517 gurl
= gurl
.ReplaceComponents(replacements
);
519 return UTF8ToWide(gurl
.PathForRequest());
522 std::wstring
GetPathAndQueryFromUrl(const std::wstring
& url
) {
523 string16 url16
= WideToUTF16(url
);
524 GURL gurl
= GURL(url16
);
525 return UTF8ToWide(gurl
.PathForRequest());
528 std::wstring
GetClipboardText() {
530 ui::Clipboard::GetForCurrentThread()->ReadText(
531 ui::Clipboard::BUFFER_STANDARD
, &text16
);
532 return UTF16ToWide(text16
);
535 void SetClipboardText(const std::wstring
& text
) {
536 ui::ScopedClipboardWriter
clipboard_writer(
537 ui::Clipboard::GetForCurrentThread(),
538 ui::Clipboard::BUFFER_STANDARD
);
539 clipboard_writer
.WriteText(WideToUTF16(text
));
542 bool AddCFMetaTag(std::string
* html_data
) {
547 std::string lower
= StringToLowerASCII(*html_data
);
548 size_t head
= lower
.find("<head>");
549 if (head
== std::string::npos
) {
550 // Add missing head section.
551 size_t html
= lower
.find("<html>");
552 if (html
!= std::string::npos
) {
553 head
= html
+ strlen("<html>");
554 html_data
->insert(head
, "<head></head>");
556 LOG(ERROR
) << "Meta tag will not be injected "
557 << "because the html tag could not be found";
560 if (head
!= std::string::npos
) {
562 head
+ strlen("<head>"),
563 "<meta http-equiv=\"x-ua-compatible\" content=\"chrome=1\" />");
565 return head
!= std::string::npos
;
568 CloseIeAtEndOfScope::~CloseIeAtEndOfScope() {
569 int closed
= CloseAllIEWindows();
570 LOG_IF(ERROR
, closed
!= 0) << "Closed " << closed
<< " windows forcefully";
573 // Attempt to connect to a running crash_service instance. Success occurs if we
574 // can actually connect to the service's pipe or we receive ERROR_PIPE_BUSY.
575 // Waits up to |timeout_ms| for success. |timeout_ms| may be 0, meaning only try
576 // once, or negative, meaning wait forever.
577 bool DetectRunningCrashService(int timeout_ms
) {
578 // Wait for the crash_service.exe to be ready for clients.
579 base::Time start
= base::Time::Now();
580 base::win::ScopedHandle new_pipe
;
583 new_pipe
.Set(::CreateFile(kCrashServicePipeName
,
584 kCrashServicePipeDesiredAccess
,
586 NULL
, // lpSecurityAttributes
588 kCrashServicePipeFlagsAndAttributes
,
589 NULL
)); // hTemplateFile
591 if (new_pipe
.IsValid()) {
595 switch (::GetLastError()) {
596 case ERROR_PIPE_BUSY
:
597 // OK, it exists, let's assume that clients will eventually be able to
600 case ERROR_FILE_NOT_FOUND
:
604 DPLOG(WARNING
) << "Unexpected error while checking crash_service.exe's "
606 // Go ahead and wait in case it clears up.
610 if (timeout_ms
== 0) {
612 } else if (timeout_ms
> 0) {
613 base::TimeDelta duration
= base::Time::Now() - start
;
614 if (duration
.InMilliseconds() > timeout_ms
) {
623 base::ProcessHandle
StartCrashService() {
624 if (DetectRunningCrashService(kCrashServiceDetectTimeoutMs
)) {
625 VLOG(1) << "crash_service.exe is already running. We will use the "
626 "existing process and leave it running after tests complete.";
630 base::FilePath exe_dir
;
631 if (!PathService::Get(base::DIR_EXE
, &exe_dir
)) {
636 base::ProcessHandle crash_service
= NULL
;
638 VLOG(1) << "Starting crash_service.exe so you know if a test crashes!";
640 base::FilePath crash_service_path
= exe_dir
.AppendASCII("crash_service.exe");
641 if (!base::LaunchProcess(crash_service_path
.value(), base::LaunchOptions(),
643 LOG(ERROR
) << "Couldn't start crash_service.exe";
647 base::Time start
= base::Time::Now();
649 if (DetectRunningCrashService(kCrashServiceStartupTimeoutMs
)) {
650 VLOG(1) << "crash_service.exe is ready for clients in "
651 << (base::Time::Now() - start
).InMilliseconds() << " ms.";
652 return crash_service
;
654 LOG(ERROR
) << "crash_service.exe failed to accept client connections "
655 "within " << kCrashServiceStartupTimeoutMs
<< " ms. "
656 "Terminating it now.";
658 // First check to see if it's even still running just to minimize the
659 // likelihood of spurious error messages from KillProcess.
660 if (WAIT_OBJECT_0
!= ::WaitForSingleObject(crash_service
, 0)) {
661 base::KillProcess(crash_service
, 0, false);
667 ScopedVirtualizeHklmAndHkcu::ScopedVirtualizeHklmAndHkcu() {
668 override_manager_
.OverrideRegistry(HKEY_LOCAL_MACHINE
, L
"hklm_fake");
669 override_manager_
.OverrideRegistry(HKEY_CURRENT_USER
, L
"hkcu_fake");
672 ScopedVirtualizeHklmAndHkcu::~ScopedVirtualizeHklmAndHkcu() {
675 void ScopedVirtualizeHklmAndHkcu::RemoveAllOverrides() {
676 override_manager_
.RemoveAllOverrides();
679 bool KillProcesses(const std::wstring
& executable_name
, int exit_code
,
682 base::NamedProcessIterator
iter(executable_name
, NULL
);
683 while (const base::ProcessEntry
* entry
= iter
.NextProcessEntry()) {
684 result
&= base::KillProcessById(entry
->pid(), exit_code
, wait
);
689 ScopedChromeFrameRegistrar::RegistrationType
GetTestBedType() {
690 if (GetConfigBool(false, L
"PerUserTestBed")) {
691 return ScopedChromeFrameRegistrar::PER_USER
;
693 return ScopedChromeFrameRegistrar::SYSTEM_LEVEL
;
697 void ClearIESessionHistory() {
698 base::FilePath session_history_path
;
699 if (!PathService::Get(base::DIR_LOCAL_APP_DATA
, &session_history_path
))
702 session_history_path
= session_history_path
.AppendASCII("Microsoft");
703 session_history_path
= session_history_path
.AppendASCII("Internet Explorer");
704 session_history_path
= session_history_path
.AppendASCII("Recovery");
705 file_util::Delete(session_history_path
, true);
708 std::string
GetLocalIPv4Address() {
710 net::NetworkInterfaceList nic_list
;
712 if (!net::GetNetworkList(&nic_list
)) {
713 LOG(ERROR
) << "GetNetworkList failed to look up non-loopback adapters. "
714 << "Tests will be run over the loopback adapter, which may "
715 << "result in hangs.";
717 // GetNetworkList only returns 'Up' non-loopback adapters. Select the first
718 // IPv4 address found - we should be able to bind/connect over it.
719 for (size_t i
= 0; i
< nic_list
.size(); ++i
) {
720 if (nic_list
[i
].address
.size() != net::kIPv4AddressSize
)
722 char* address_string
=
723 inet_ntoa(*reinterpret_cast<in_addr
*>(&nic_list
[i
].address
[0]));
724 DCHECK(address_string
!= NULL
);
725 if (address_string
!= NULL
) {
726 LOG(INFO
) << "HTTP tests will run over " << address_string
<< ".";
727 address
.assign(address_string
);
733 if (address
.empty()) {
734 LOG(ERROR
) << "Failed to find a non-loopback IP_V4 address. Tests will be "
735 << "run over the loopback adapter, which may result in hangs.";
736 address
.assign("127.0.0.1");
742 } // namespace chrome_frame_test