blob: 99fedd22721045fdbbf67b2c5a0610ad6d6f0e1d [file] [log] [blame]
[email protected]bc73b4e52010-03-26 04:16:201// Copyright (c) 2010 The Chromium Authors. All rights reserved.
[email protected]f7817822009-09-24 05:11:582// 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 <atlbase.h>
8#include <atlwin.h>
9#include <iepmapi.h>
[email protected]42d7c022009-11-20 22:44:5810#include <sddl.h>
[email protected]f7817822009-09-24 05:11:5811
[email protected]5d91c9e2010-07-28 17:25:2812#include "base/command_line.h"
[email protected]59aa617b2010-09-02 22:02:0913#include "base/file_path.h"
[email protected]d8b83b212010-04-07 18:27:5214#include "base/file_version_info.h"
[email protected]d23ca372010-02-16 20:36:0915#include "base/path_service.h"
[email protected]d9f92192010-06-23 14:51:3216#include "base/process_util.h"
[email protected]f7817822009-09-24 05:11:5817#include "base/scoped_handle.h"
[email protected]59aa617b2010-09-02 22:02:0918#include "base/scoped_ptr.h"
[email protected]5d91c9e2010-07-28 17:25:2819#include "base/string_util.h"
[email protected]d3451d832010-10-01 11:17:3720#include "base/stringprintf.h"
[email protected]d55194ca2010-03-11 18:25:4521#include "base/utf_string_conversions.h"
[email protected]f7817822009-09-24 05:11:5822#include "base/win_util.h"
[email protected]2d6503982010-10-17 04:41:5423#include "base/win/registry.h"
[email protected]935aa542010-10-15 01:59:1524#include "base/win/windows_version.h"
[email protected]f7817822009-09-24 05:11:5825#include "chrome/common/chrome_switches.h"
[email protected]bc73b4e52010-03-26 04:16:2026#include "chrome/common/chrome_paths.h"
27#include "chrome/common/chrome_paths_internal.h"
[email protected]7bc272f2009-12-09 01:09:2828#include "chrome_frame/utils.h"
[email protected]bc73b4e52010-03-26 04:16:2029
[email protected]f7817822009-09-24 05:11:5830namespace chrome_frame_test {
31
[email protected]2014db002010-09-28 13:46:1032const wchar_t kCrashServicePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices";
33
34const DWORD kCrashServicePipeDesiredAccess = FILE_READ_DATA |
35 FILE_WRITE_DATA |
36 FILE_WRITE_ATTRIBUTES;
37
38const DWORD kCrashServicePipeFlagsAndAttributes = SECURITY_IDENTIFICATION |
39 SECURITY_SQOS_PRESENT;
40const int kCrashServiceStartupTimeoutMs = 500;
41
[email protected]f7817822009-09-24 05:11:5842const wchar_t kIEImageName[] = L"iexplore.exe";
43const wchar_t kIEBrokerImageName[] = L"ieuser.exe";
44const wchar_t kFirefoxImageName[] = L"firefox.exe";
45const wchar_t kOperaImageName[] = L"opera.exe";
46const wchar_t kSafariImageName[] = L"safari.exe";
[email protected]bcd840c2010-05-27 10:43:0047const char kChromeImageName[] = "chrome.exe";
[email protected]d8b83b212010-04-07 18:27:5248const wchar_t kIEProfileName[] = L"iexplore";
[email protected]128f9d92010-04-12 15:58:5249const wchar_t kChromeLauncher[] = L"chrome_launcher.exe";
[email protected]ba443b72010-05-14 23:34:3850const int kChromeFrameLongNavigationTimeoutInSeconds = 10;
[email protected]f7817822009-09-24 05:11:5851
[email protected]f7817822009-09-24 05:11:5852// Callback function for EnumThreadWindows.
53BOOL CALLBACK CloseWindowsThreadCallback(HWND hwnd, LPARAM param) {
54 int& count = *reinterpret_cast<int*>(param);
55 if (IsWindowVisible(hwnd)) {
56 if (IsWindowEnabled(hwnd)) {
57 DWORD results = 0;
58 if (!::SendMessageTimeout(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0, SMTO_BLOCK,
59 10000, &results)) {
[email protected]d3451d832010-10-01 11:17:3760 LOG(WARNING) << "Window hung: " << base::StringPrintf(L"%08X", hwnd);
[email protected]f7817822009-09-24 05:11:5861 }
62 count++;
63 } else {
64 DLOG(WARNING) << "Skipping disabled window: "
[email protected]d3451d832010-10-01 11:17:3765 << base::StringPrintf(L"%08X", hwnd);
[email protected]f7817822009-09-24 05:11:5866 }
67 }
68 return TRUE; // continue enumeration
69}
70
71// Attempts to close all non-child, visible windows on the given thread.
72// The return value is the number of visible windows a close request was
73// sent to.
74int CloseVisibleTopLevelWindowsOnThread(DWORD thread_id) {
75 int window_close_attempts = 0;
76 EnumThreadWindows(thread_id, CloseWindowsThreadCallback,
77 reinterpret_cast<LPARAM>(&window_close_attempts));
78 return window_close_attempts;
79}
80
81// Enumerates the threads of a process and attempts to close visible non-child
82// windows on all threads of the process.
83// The return value is the number of visible windows a close request was
84// sent to.
85int CloseVisibleWindowsOnAllThreads(HANDLE process) {
86 DWORD process_id = ::GetProcessId(process);
87 if (process_id == 0) {
88 NOTREACHED();
89 return 0;
90 }
91
92 ScopedHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0));
93 if (!snapshot.IsValid()) {
94 NOTREACHED();
95 return 0;
96 }
97
98 int window_close_attempts = 0;
99 THREADENTRY32 te = { sizeof(THREADENTRY32) };
100 if (Thread32First(snapshot, &te)) {
101 do {
102 if (RTL_CONTAINS_FIELD(&te, te.dwSize, th32OwnerProcessID) &&
103 te.th32OwnerProcessID == process_id) {
104 window_close_attempts +=
105 CloseVisibleTopLevelWindowsOnThread(te.th32ThreadID);
106 }
107 te.dwSize = sizeof(te);
108 } while (Thread32Next(snapshot, &te));
109 }
110
111 return window_close_attempts;
112}
113
[email protected]f7817822009-09-24 05:11:58114std::wstring GetExecutableAppPath(const std::wstring& file) {
115 std::wstring kAppPathsKey =
116 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\";
117
118 std::wstring app_path;
[email protected]2d6503982010-10-17 04:41:54119 base::win::RegKey key(HKEY_LOCAL_MACHINE, (kAppPathsKey + file).c_str(),
120 KEY_READ);
[email protected]f7817822009-09-24 05:11:58121 if (key.Handle()) {
122 key.ReadValue(NULL, &app_path);
123 }
124
125 return app_path;
126}
127
128std::wstring FormatCommandForApp(const std::wstring& exe_name,
129 const std::wstring& argument) {
[email protected]d3451d832010-10-01 11:17:37130 std::wstring reg_path(
131 base::StringPrintf(L"Applications\\%ls\\shell\\open\\command",
132 exe_name.c_str()));
[email protected]2d6503982010-10-17 04:41:54133 base::win::RegKey key(HKEY_CLASSES_ROOT, reg_path.c_str(), KEY_READ);
[email protected]f7817822009-09-24 05:11:58134
135 std::wstring command;
136 if (key.Handle()) {
137 key.ReadValue(NULL, &command);
138 int found = command.find(L"%1");
139 if (found >= 0) {
140 command.replace(found, 2, argument);
141 }
142 }
143 return command;
144}
145
146base::ProcessHandle LaunchExecutable(const std::wstring& executable,
147 const std::wstring& argument) {
148 base::ProcessHandle process = NULL;
149 std::wstring path = GetExecutableAppPath(executable);
150 if (path.empty()) {
151 path = FormatCommandForApp(executable, argument);
152 if (path.empty()) {
[email protected]7ec1f4e2010-05-19 18:43:21153 LOG(ERROR) << "Failed to find executable: " << executable;
[email protected]f7817822009-09-24 05:11:58154 } else {
[email protected]51343d5a2009-10-26 22:39:33155 CommandLine cmdline = CommandLine::FromString(path);
[email protected]7ec1f4e2010-05-19 18:43:21156 if (!base::LaunchApp(cmdline, false, false, &process)) {
157 LOG(ERROR) << "LaunchApp failed: " << ::GetLastError();
158 }
[email protected]f7817822009-09-24 05:11:58159 }
160 } else {
[email protected]d1540c82009-10-27 00:18:32161 CommandLine cmdline((FilePath(path)));
[email protected]0445eb42010-08-13 22:10:30162 cmdline.AppendArgNative(argument);
[email protected]7ec1f4e2010-05-19 18:43:21163 if (!base::LaunchApp(cmdline, false, false, &process)) {
164 LOG(ERROR) << "LaunchApp failed: " << ::GetLastError();
165 }
[email protected]f7817822009-09-24 05:11:58166 }
167 return process;
168}
169
170base::ProcessHandle LaunchFirefox(const std::wstring& url) {
171 return LaunchExecutable(kFirefoxImageName, url);
172}
173
174base::ProcessHandle LaunchSafari(const std::wstring& url) {
175 return LaunchExecutable(kSafariImageName, url);
176}
177
178base::ProcessHandle LaunchChrome(const std::wstring& url) {
[email protected]bcd840c2010-05-27 10:43:00179 FilePath path;
[email protected]d23ca372010-02-16 20:36:09180 PathService::Get(base::DIR_MODULE, &path);
[email protected]bcd840c2010-05-27 10:43:00181 path = path.AppendASCII(kChromeImageName);
[email protected]d23ca372010-02-16 20:36:09182
[email protected]bcd840c2010-05-27 10:43:00183 CommandLine cmd(path);
[email protected]0445eb42010-08-13 22:10:30184 cmd.AppendSwitch(switches::kNoFirstRun);
185 cmd.AppendArgNative(url);
[email protected]d23ca372010-02-16 20:36:09186
187 base::ProcessHandle process = NULL;
188 base::LaunchApp(cmd, false, false, &process);
189 return process;
[email protected]f7817822009-09-24 05:11:58190}
191
192base::ProcessHandle LaunchOpera(const std::wstring& url) {
193 // NOTE: For Opera tests to work it must be configured to start up with
194 // a blank page. There is an command line switch, -nosession, that's supposed
195 // to avoid opening up the previous session, but that switch is not working.
196 // TODO(tommi): Include a special ini file (opera6.ini) for opera and launch
197 // with our required settings. This file is by default stored here:
198 // "%USERPROFILE%\Application Data\Opera\Opera\profile\opera6.ini"
199 return LaunchExecutable(kOperaImageName, url);
200}
201
202base::ProcessHandle LaunchIEOnVista(const std::wstring& url) {
203 typedef HRESULT (WINAPI* IELaunchURLPtr)(
204 const wchar_t* url,
205 PROCESS_INFORMATION *pi,
206 VOID *info);
207
208 IELaunchURLPtr launch;
209 PROCESS_INFORMATION pi = {0};
210 IELAUNCHURLINFO info = {sizeof info, 0};
211 HMODULE h = LoadLibrary(L"ieframe.dll");
[email protected]7ec1f4e2010-05-19 18:43:21212 if (!h) {
213 LOG(ERROR) << "Failed to load ieframe.dll: " << ::GetLastError();
[email protected]f7817822009-09-24 05:11:58214 return NULL;
[email protected]7ec1f4e2010-05-19 18:43:21215 }
[email protected]f7817822009-09-24 05:11:58216 launch = reinterpret_cast<IELaunchURLPtr>(GetProcAddress(h, "IELaunchURL"));
[email protected]7ec1f4e2010-05-19 18:43:21217 CHECK(launch);
[email protected]f7817822009-09-24 05:11:58218 HRESULT hr = launch(url.c_str(), &pi, &info);
219 FreeLibrary(h);
[email protected]7ec1f4e2010-05-19 18:43:21220 if (SUCCEEDED(hr)) {
[email protected]f7817822009-09-24 05:11:58221 CloseHandle(pi.hThread);
[email protected]7ec1f4e2010-05-19 18:43:21222 } else {
[email protected]d3451d832010-10-01 11:17:37223 LOG(ERROR) << base::StringPrintf("IELaunchURL failed: 0x%08X", hr);
[email protected]7ec1f4e2010-05-19 18:43:21224 }
[email protected]f7817822009-09-24 05:11:58225 return pi.hProcess;
226}
227
228base::ProcessHandle LaunchIE(const std::wstring& url) {
[email protected]935aa542010-10-15 01:59:15229 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
[email protected]f7817822009-09-24 05:11:58230 return LaunchIEOnVista(url);
[email protected]f7817822009-09-24 05:11:58231 }
[email protected]935aa542010-10-15 01:59:15232 return LaunchExecutable(kIEImageName, url);
[email protected]f7817822009-09-24 05:11:58233}
234
235int CloseAllIEWindows() {
236 int ret = 0;
237
238 ScopedComPtr<IShellWindows> windows;
239 HRESULT hr = ::CoCreateInstance(__uuidof(ShellWindows), NULL, CLSCTX_ALL,
240 IID_IShellWindows, reinterpret_cast<void**>(windows.Receive()));
241 DCHECK(SUCCEEDED(hr));
242
243 if (SUCCEEDED(hr)) {
244 long count = 0; // NOLINT
245 windows->get_Count(&count);
246 VARIANT i = { VT_I4 };
247 for (i.lVal = 0; i.lVal < count; ++i.lVal) {
248 ScopedComPtr<IDispatch> folder;
249 windows->Item(i, folder.Receive());
250 if (folder != NULL) {
251 ScopedComPtr<IWebBrowser2> browser;
252 if (SUCCEEDED(browser.QueryFrom(folder))) {
[email protected]59aa617b2010-09-02 22:02:09253 bool is_ie = true;
254 HWND window = NULL;
255 // Check the class of the browser window to make sure we only close
256 // IE windows.
257 if (browser->get_HWND(reinterpret_cast<SHANDLE_PTR*>(window))) {
258 wchar_t class_name[MAX_PATH];
259 if (::GetClassName(window, class_name, arraysize(class_name))) {
260 is_ie = _wcsicmp(class_name, L"IEFrame") == 0;
261 }
262 }
263 if (is_ie) {
264 browser->Quit();
265 ++ret;
266 }
[email protected]f7817822009-09-24 05:11:58267 }
268 }
269 }
270 }
271
272 return ret;
273}
274
[email protected]42d7c022009-11-20 22:44:58275
276LowIntegrityToken::LowIntegrityToken() : impersonated_(false) {
277}
278
279LowIntegrityToken::~LowIntegrityToken() {
280 RevertToSelf();
281}
282
283BOOL LowIntegrityToken::RevertToSelf() {
284 BOOL ok = TRUE;
285 if (impersonated_) {
286 DCHECK(IsImpersonated());
287 ok = ::RevertToSelf();
288 if (ok)
289 impersonated_ = false;
290 }
291
292 return ok;
293}
294
295BOOL LowIntegrityToken::Impersonate() {
296 DCHECK(!impersonated_);
297 DCHECK(!IsImpersonated());
298 HANDLE process_token_handle = NULL;
299 BOOL ok = ::OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE,
300 &process_token_handle);
301 if (!ok) {
302 DLOG(ERROR) << "::OpenProcessToken failed: " << GetLastError();
303 return ok;
304 }
305
306 ScopedHandle process_token(process_token_handle);
307 // Create impersonation low integrity token.
308 HANDLE impersonation_token_handle = NULL;
309 ok = ::DuplicateTokenEx(process_token,
310 TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_ADJUST_DEFAULT, NULL,
311 SecurityImpersonation, TokenImpersonation, &impersonation_token_handle);
312 if (!ok) {
313 DLOG(ERROR) << "::DuplicateTokenEx failed: " << GetLastError();
314 return ok;
315 }
316
[email protected]7ec1f4e2010-05-19 18:43:21317 // TODO(stoyan): sandbox/src/restricted_token_utils.cc has
318 // SetTokenIntegrityLevel function already.
[email protected]42d7c022009-11-20 22:44:58319 ScopedHandle impersonation_token(impersonation_token_handle);
320 PSID integrity_sid = NULL;
321 TOKEN_MANDATORY_LABEL tml = {0};
322 ok = ::ConvertStringSidToSid(SDDL_ML_LOW, &integrity_sid);
323 if (!ok) {
324 DLOG(ERROR) << "::ConvertStringSidToSid failed: " << GetLastError();
325 return ok;
326 }
327
328 tml.Label.Attributes = SE_GROUP_INTEGRITY | SE_GROUP_INTEGRITY_ENABLED;
329 tml.Label.Sid = integrity_sid;
330 ok = ::SetTokenInformation(impersonation_token, TokenIntegrityLevel,
331 &tml, sizeof(tml) + ::GetLengthSid(integrity_sid));
332 ::LocalFree(integrity_sid);
333 if (!ok) {
334 DLOG(ERROR) << "::SetTokenInformation failed: " << GetLastError();
335 return ok;
336 }
337
338 // Switch current thread to low integrity.
339 ok = ::ImpersonateLoggedOnUser(impersonation_token);
340 if (ok) {
341 impersonated_ = true;
342 } else {
343 DLOG(ERROR) << "::ImpersonateLoggedOnUser failed: " << GetLastError();
344 }
345
346 return ok;
347}
348
349bool LowIntegrityToken::IsImpersonated() {
350 HANDLE token = NULL;
351 if (!::OpenThreadToken(::GetCurrentThread(), 0, false, &token) &&
352 ::GetLastError() != ERROR_NO_TOKEN) {
353 return true;
354 }
355
356 if (token)
357 ::CloseHandle(token);
358
359 return false;
360}
361
[email protected]7bc272f2009-12-09 01:09:28362HRESULT LaunchIEAsComServer(IWebBrowser2** web_browser) {
363 if (!web_browser)
364 return E_INVALIDARG;
365
[email protected]7aa8d972010-02-17 04:24:49366 AllowSetForegroundWindow(ASFW_ANY);
367
[email protected]7bc272f2009-12-09 01:09:28368 HRESULT hr = S_OK;
369 DWORD cocreate_flags = CLSCTX_LOCAL_SERVER;
370 chrome_frame_test::LowIntegrityToken token;
371 // Vista has a bug which manifests itself when a medium integrity process
372 // launches a COM server like IE which runs in protected mode due to UAC.
373 // This causes the IWebBrowser2 interface which is returned to be useless,
374 // i.e it does not receive any events, etc. Our workaround for this is
375 // to impersonate a low integrity token and then launch IE.
[email protected]935aa542010-10-15 01:59:15376 if (base::win::GetVersion() == base::win::VERSION_VISTA &&
[email protected]6448b3ab2010-07-12 22:07:03377 GetInstalledIEVersion() == IE_7) {
[email protected]7bc272f2009-12-09 01:09:28378 // Create medium integrity browser that will launch IE broker.
379 ScopedComPtr<IWebBrowser2> medium_integrity_browser;
380 hr = medium_integrity_browser.CreateInstance(CLSID_InternetExplorer, NULL,
381 CLSCTX_LOCAL_SERVER);
382 if (FAILED(hr))
383 return hr;
384 medium_integrity_browser->Quit();
385 // Broker remains alive.
386 if (!token.Impersonate()) {
387 hr = HRESULT_FROM_WIN32(GetLastError());
388 return hr;
389 }
390
391 cocreate_flags |= CLSCTX_ENABLE_CLOAKING;
392 }
393
394 hr = ::CoCreateInstance(CLSID_InternetExplorer, NULL,
395 cocreate_flags, IID_IWebBrowser2,
396 reinterpret_cast<void**>(web_browser));
397 // ~LowIntegrityToken() will switch integrity back to medium.
398 return hr;
399}
400
[email protected]bc73b4e52010-03-26 04:16:20401FilePath GetProfilePath(const std::wstring& profile_name) {
402 FilePath profile_path;
403 chrome::GetChromeFrameUserDataDirectory(&profile_path);
404 return profile_path.Append(profile_name);
405}
406
[email protected]1da35172010-04-22 02:30:03407std::wstring GetExeVersion(const std::wstring& exe_path) {
408 scoped_ptr<FileVersionInfo> ie_version_info(
409 FileVersionInfo::CreateFileVersionInfo(FilePath(exe_path)));
410 return ie_version_info->product_version();
411}
412
413IEVersion GetInstalledIEVersion() {
414 std::wstring path = chrome_frame_test::GetExecutableAppPath(kIEImageName);
415 std::wstring version = GetExeVersion(path);
416
417 switch (version[0]) {
418 case '6':
419 return IE_6;
420 case '7':
421 return IE_7;
422 case '8':
423 return IE_8;
[email protected]d8e13512010-09-22 17:02:58424 case '9':
425 return IE_9;
[email protected]1da35172010-04-22 02:30:03426 default:
427 break;
428 }
429
430 return IE_UNSUPPORTED;
431}
432
[email protected]d8b83b212010-04-07 18:27:52433FilePath GetProfilePathForIE() {
434 FilePath profile_path;
435 // Browsers without IDeleteBrowsingHistory in non-priv mode
436 // have their profiles moved into "Temporary Internet Files".
437 // The code below basically retrieves the version of IE and computes
438 // the profile directory accordingly.
[email protected]d8e13512010-09-22 17:02:58439 if (GetInstalledIEVersion() >= IE_8) {
[email protected]d8b83b212010-04-07 18:27:52440 profile_path = GetProfilePath(kIEProfileName);
441 } else {
442 profile_path = GetIETemporaryFilesFolder();
443 profile_path = profile_path.Append(L"Google Chrome Frame");
444 }
445 return profile_path;
446}
447
[email protected]6448b3ab2010-07-12 22:07:03448FilePath GetTestDataFolder() {
449 FilePath test_dir;
450 PathService::Get(base::DIR_SOURCE_ROOT, &test_dir);
451 test_dir = test_dir.Append(FILE_PATH_LITERAL("chrome_frame"))
452 .Append(FILE_PATH_LITERAL("test"))
453 .Append(FILE_PATH_LITERAL("data"));
454 return test_dir;
455}
456
457std::wstring GetPathFromUrl(const std::wstring& url) {
458 string16 url16 = WideToUTF16(url);
459 GURL gurl = GURL(url16);
460 if (gurl.has_query()) {
461 GURL::Replacements replacements;
462 replacements.ClearQuery();
463 gurl = gurl.ReplaceComponents(replacements);
464 }
465 return UTF8ToWide(gurl.PathForRequest());
466}
467
468std::wstring GetPathAndQueryFromUrl(const std::wstring& url) {
469 string16 url16 = WideToUTF16(url);
470 GURL gurl = GURL(url16);
471 return UTF8ToWide(gurl.PathForRequest());
472}
473
474bool AddCFMetaTag(std::string* html_data) {
475 if (!html_data) {
476 NOTREACHED();
477 return false;
478 }
[email protected]59aa617b2010-09-02 22:02:09479 std::string lower = StringToLowerASCII(*html_data);
480 size_t head = lower.find("<head>");
[email protected]6448b3ab2010-07-12 22:07:03481 if (head == std::string::npos) {
482 // Add missing head section.
[email protected]59aa617b2010-09-02 22:02:09483 size_t html = lower.find("<html>");
[email protected]6448b3ab2010-07-12 22:07:03484 if (html != std::string::npos) {
[email protected]59aa617b2010-09-02 22:02:09485 head = html + strlen("<html>");
486 html_data->insert(head, "<head></head>");
487 } else {
488 DLOG(ERROR) << "Meta tag will not be injected "
489 << "because the html tag could not be found";
[email protected]6448b3ab2010-07-12 22:07:03490 }
491 }
492 if (head != std::string::npos) {
493 html_data->insert(
494 head + strlen("<head>"),
495 "<meta http-equiv=\"x-ua-compatible\" content=\"chrome=1\" />");
496 }
497 return head != std::string::npos;
498}
499
[email protected]5d91c9e2010-07-28 17:25:28500CloseIeAtEndOfScope::~CloseIeAtEndOfScope() {
501 int closed = CloseAllIEWindows();
502 DLOG_IF(ERROR, closed != 0) << "Closed " << closed << " windows forcefully";
503}
504
[email protected]2014db002010-09-28 13:46:10505// Attempt to connect to a running crash_service instance. Success occurs if we
506// can actually connect to the service's pipe or we receive ERROR_PIPE_BUSY.
507// Waits up to |timeout_ms| for success. |timeout_ms| may be 0, meaning only try
508// once, or negative, meaning wait forever.
509bool DetectRunningCrashService(int timeout_ms) {
510 // Wait for the crash_service.exe to be ready for clients.
511 base::Time start = base::Time::Now();
512 ScopedHandle new_pipe;
513
514 while (true) {
515 new_pipe.Set(::CreateFile(kCrashServicePipeName,
516 kCrashServicePipeDesiredAccess,
517 0, // dwShareMode
518 NULL, // lpSecurityAttributes
519 OPEN_EXISTING,
520 kCrashServicePipeFlagsAndAttributes,
521 NULL)); // hTemplateFile
522
523 if (new_pipe.IsValid()) {
524 return true;
525 }
526
527 switch (::GetLastError()) {
528 case ERROR_PIPE_BUSY:
529 // OK, it exists, let's assume that clients will eventually be able to
530 // connect to it.
531 return true;
532 case ERROR_FILE_NOT_FOUND:
533 // Wait a bit longer
534 break;
535 default:
536 DLOG(WARNING) << "Unexpected error while checking crash_service.exe's "
537 << "pipe: " << win_util::FormatLastWin32Error();
538 // Go ahead and wait in case it clears up.
539 break;
540 }
541
542 if (timeout_ms == 0) {
543 return false;
544 } else if (timeout_ms > 0) {
545 base::TimeDelta duration = base::Time::Now() - start;
546 if (duration.InMilliseconds() > timeout_ms) {
547 return false;
548 }
549 }
550
551 Sleep(10);
552 }
553}
554
[email protected]d9f92192010-06-23 14:51:32555base::ProcessHandle StartCrashService() {
[email protected]2014db002010-09-28 13:46:10556 if (DetectRunningCrashService(kCrashServiceStartupTimeoutMs)) {
557 DLOG(INFO) << "crash_service.exe is already running. We will use the "
558 << "existing process and leave it running after tests complete.";
559 return NULL;
560 }
561
[email protected]d9f92192010-06-23 14:51:32562 FilePath exe_dir;
563 if (!PathService::Get(base::DIR_EXE, &exe_dir)) {
564 DCHECK(false);
565 return NULL;
566 }
567
568 base::ProcessHandle crash_service = NULL;
569
[email protected]2014db002010-09-28 13:46:10570 DLOG(INFO) << "Starting crash_service.exe so you know if a test crashes!";
571
[email protected]d9f92192010-06-23 14:51:32572 FilePath crash_service_path = exe_dir.AppendASCII("crash_service.exe");
573 if (!base::LaunchApp(crash_service_path.value(), false, false,
574 &crash_service)) {
575 DLOG(ERROR) << "Couldn't start crash_service.exe";
576 return NULL;
577 }
578
[email protected]2014db002010-09-28 13:46:10579 base::Time start = base::Time::Now();
580
581 if (DetectRunningCrashService(kCrashServiceStartupTimeoutMs)) {
582 DLOG(INFO) << "crash_service.exe is ready for clients in "
583 << (base::Time::Now() - start).InMilliseconds() << "ms.";
584 return crash_service;
585 } else {
586 DLOG(ERROR) << "crash_service.exe failed to accept client connections "
587 << "within " << kCrashServiceStartupTimeoutMs << "ms. "
588 << "Terminating it now.";
589
590 // First check to see if it's even still running just to minimize the
591 // likelihood of spurious error messages from KillProcess.
592 if (WAIT_OBJECT_0 != ::WaitForSingleObject(crash_service, 0)) {
593 base::KillProcess(crash_service, 0, false);
594 }
595 return NULL;
596 }
[email protected]d9f92192010-06-23 14:51:32597}
598
[email protected]f7817822009-09-24 05:11:58599} // namespace chrome_frame_test