blob: e98d3539994a47e0a9fc473dfe6c54a04589bbbb [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/registry.h" // to find IE and firefox
18#include "base/scoped_handle.h"
[email protected]59aa617b2010-09-02 22:02:0919#include "base/scoped_ptr.h"
[email protected]5d91c9e2010-07-28 17:25:2820#include "base/string_util.h"
[email protected]d3451d832010-10-01 11:17:3721#include "base/stringprintf.h"
[email protected]d55194ca2010-03-11 18:25:4522#include "base/utf_string_conversions.h"
[email protected]f7817822009-09-24 05:11:5823#include "base/win_util.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]87e700622010-08-14 02:13:43119 RegKey key(HKEY_LOCAL_MACHINE, (kAppPathsKey + file).c_str(), KEY_READ);
[email protected]f7817822009-09-24 05:11:58120 if (key.Handle()) {
121 key.ReadValue(NULL, &app_path);
122 }
123
124 return app_path;
125}
126
127std::wstring FormatCommandForApp(const std::wstring& exe_name,
128 const std::wstring& argument) {
[email protected]d3451d832010-10-01 11:17:37129 std::wstring reg_path(
130 base::StringPrintf(L"Applications\\%ls\\shell\\open\\command",
131 exe_name.c_str()));
[email protected]87e700622010-08-14 02:13:43132 RegKey key(HKEY_CLASSES_ROOT, reg_path.c_str(), KEY_READ);
[email protected]f7817822009-09-24 05:11:58133
134 std::wstring command;
135 if (key.Handle()) {
136 key.ReadValue(NULL, &command);
137 int found = command.find(L"%1");
138 if (found >= 0) {
139 command.replace(found, 2, argument);
140 }
141 }
142 return command;
143}
144
145base::ProcessHandle LaunchExecutable(const std::wstring& executable,
146 const std::wstring& argument) {
147 base::ProcessHandle process = NULL;
148 std::wstring path = GetExecutableAppPath(executable);
149 if (path.empty()) {
150 path = FormatCommandForApp(executable, argument);
151 if (path.empty()) {
[email protected]7ec1f4e2010-05-19 18:43:21152 LOG(ERROR) << "Failed to find executable: " << executable;
[email protected]f7817822009-09-24 05:11:58153 } else {
[email protected]51343d5a2009-10-26 22:39:33154 CommandLine cmdline = CommandLine::FromString(path);
[email protected]7ec1f4e2010-05-19 18:43:21155 if (!base::LaunchApp(cmdline, false, false, &process)) {
156 LOG(ERROR) << "LaunchApp failed: " << ::GetLastError();
157 }
[email protected]f7817822009-09-24 05:11:58158 }
159 } else {
[email protected]d1540c82009-10-27 00:18:32160 CommandLine cmdline((FilePath(path)));
[email protected]0445eb42010-08-13 22:10:30161 cmdline.AppendArgNative(argument);
[email protected]7ec1f4e2010-05-19 18:43:21162 if (!base::LaunchApp(cmdline, false, false, &process)) {
163 LOG(ERROR) << "LaunchApp failed: " << ::GetLastError();
164 }
[email protected]f7817822009-09-24 05:11:58165 }
166 return process;
167}
168
169base::ProcessHandle LaunchFirefox(const std::wstring& url) {
170 return LaunchExecutable(kFirefoxImageName, url);
171}
172
173base::ProcessHandle LaunchSafari(const std::wstring& url) {
174 return LaunchExecutable(kSafariImageName, url);
175}
176
177base::ProcessHandle LaunchChrome(const std::wstring& url) {
[email protected]bcd840c2010-05-27 10:43:00178 FilePath path;
[email protected]d23ca372010-02-16 20:36:09179 PathService::Get(base::DIR_MODULE, &path);
[email protected]bcd840c2010-05-27 10:43:00180 path = path.AppendASCII(kChromeImageName);
[email protected]d23ca372010-02-16 20:36:09181
[email protected]bcd840c2010-05-27 10:43:00182 CommandLine cmd(path);
[email protected]0445eb42010-08-13 22:10:30183 cmd.AppendSwitch(switches::kNoFirstRun);
184 cmd.AppendArgNative(url);
[email protected]d23ca372010-02-16 20:36:09185
186 base::ProcessHandle process = NULL;
187 base::LaunchApp(cmd, false, false, &process);
188 return process;
[email protected]f7817822009-09-24 05:11:58189}
190
191base::ProcessHandle LaunchOpera(const std::wstring& url) {
192 // NOTE: For Opera tests to work it must be configured to start up with
193 // a blank page. There is an command line switch, -nosession, that's supposed
194 // to avoid opening up the previous session, but that switch is not working.
195 // TODO(tommi): Include a special ini file (opera6.ini) for opera and launch
196 // with our required settings. This file is by default stored here:
197 // "%USERPROFILE%\Application Data\Opera\Opera\profile\opera6.ini"
198 return LaunchExecutable(kOperaImageName, url);
199}
200
201base::ProcessHandle LaunchIEOnVista(const std::wstring& url) {
202 typedef HRESULT (WINAPI* IELaunchURLPtr)(
203 const wchar_t* url,
204 PROCESS_INFORMATION *pi,
205 VOID *info);
206
207 IELaunchURLPtr launch;
208 PROCESS_INFORMATION pi = {0};
209 IELAUNCHURLINFO info = {sizeof info, 0};
210 HMODULE h = LoadLibrary(L"ieframe.dll");
[email protected]7ec1f4e2010-05-19 18:43:21211 if (!h) {
212 LOG(ERROR) << "Failed to load ieframe.dll: " << ::GetLastError();
[email protected]f7817822009-09-24 05:11:58213 return NULL;
[email protected]7ec1f4e2010-05-19 18:43:21214 }
[email protected]f7817822009-09-24 05:11:58215 launch = reinterpret_cast<IELaunchURLPtr>(GetProcAddress(h, "IELaunchURL"));
[email protected]7ec1f4e2010-05-19 18:43:21216 CHECK(launch);
[email protected]f7817822009-09-24 05:11:58217 HRESULT hr = launch(url.c_str(), &pi, &info);
218 FreeLibrary(h);
[email protected]7ec1f4e2010-05-19 18:43:21219 if (SUCCEEDED(hr)) {
[email protected]f7817822009-09-24 05:11:58220 CloseHandle(pi.hThread);
[email protected]7ec1f4e2010-05-19 18:43:21221 } else {
[email protected]d3451d832010-10-01 11:17:37222 LOG(ERROR) << base::StringPrintf("IELaunchURL failed: 0x%08X", hr);
[email protected]7ec1f4e2010-05-19 18:43:21223 }
[email protected]f7817822009-09-24 05:11:58224 return pi.hProcess;
225}
226
227base::ProcessHandle LaunchIE(const std::wstring& url) {
[email protected]935aa542010-10-15 01:59:15228 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
[email protected]f7817822009-09-24 05:11:58229 return LaunchIEOnVista(url);
[email protected]f7817822009-09-24 05:11:58230 }
[email protected]935aa542010-10-15 01:59:15231 return LaunchExecutable(kIEImageName, url);
[email protected]f7817822009-09-24 05:11:58232}
233
234int CloseAllIEWindows() {
235 int ret = 0;
236
237 ScopedComPtr<IShellWindows> windows;
238 HRESULT hr = ::CoCreateInstance(__uuidof(ShellWindows), NULL, CLSCTX_ALL,
239 IID_IShellWindows, reinterpret_cast<void**>(windows.Receive()));
240 DCHECK(SUCCEEDED(hr));
241
242 if (SUCCEEDED(hr)) {
243 long count = 0; // NOLINT
244 windows->get_Count(&count);
245 VARIANT i = { VT_I4 };
246 for (i.lVal = 0; i.lVal < count; ++i.lVal) {
247 ScopedComPtr<IDispatch> folder;
248 windows->Item(i, folder.Receive());
249 if (folder != NULL) {
250 ScopedComPtr<IWebBrowser2> browser;
251 if (SUCCEEDED(browser.QueryFrom(folder))) {
[email protected]59aa617b2010-09-02 22:02:09252 bool is_ie = true;
253 HWND window = NULL;
254 // Check the class of the browser window to make sure we only close
255 // IE windows.
256 if (browser->get_HWND(reinterpret_cast<SHANDLE_PTR*>(window))) {
257 wchar_t class_name[MAX_PATH];
258 if (::GetClassName(window, class_name, arraysize(class_name))) {
259 is_ie = _wcsicmp(class_name, L"IEFrame") == 0;
260 }
261 }
262 if (is_ie) {
263 browser->Quit();
264 ++ret;
265 }
[email protected]f7817822009-09-24 05:11:58266 }
267 }
268 }
269 }
270
271 return ret;
272}
273
[email protected]42d7c022009-11-20 22:44:58274
275LowIntegrityToken::LowIntegrityToken() : impersonated_(false) {
276}
277
278LowIntegrityToken::~LowIntegrityToken() {
279 RevertToSelf();
280}
281
282BOOL LowIntegrityToken::RevertToSelf() {
283 BOOL ok = TRUE;
284 if (impersonated_) {
285 DCHECK(IsImpersonated());
286 ok = ::RevertToSelf();
287 if (ok)
288 impersonated_ = false;
289 }
290
291 return ok;
292}
293
294BOOL LowIntegrityToken::Impersonate() {
295 DCHECK(!impersonated_);
296 DCHECK(!IsImpersonated());
297 HANDLE process_token_handle = NULL;
298 BOOL ok = ::OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE,
299 &process_token_handle);
300 if (!ok) {
301 DLOG(ERROR) << "::OpenProcessToken failed: " << GetLastError();
302 return ok;
303 }
304
305 ScopedHandle process_token(process_token_handle);
306 // Create impersonation low integrity token.
307 HANDLE impersonation_token_handle = NULL;
308 ok = ::DuplicateTokenEx(process_token,
309 TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_ADJUST_DEFAULT, NULL,
310 SecurityImpersonation, TokenImpersonation, &impersonation_token_handle);
311 if (!ok) {
312 DLOG(ERROR) << "::DuplicateTokenEx failed: " << GetLastError();
313 return ok;
314 }
315
[email protected]7ec1f4e2010-05-19 18:43:21316 // TODO(stoyan): sandbox/src/restricted_token_utils.cc has
317 // SetTokenIntegrityLevel function already.
[email protected]42d7c022009-11-20 22:44:58318 ScopedHandle impersonation_token(impersonation_token_handle);
319 PSID integrity_sid = NULL;
320 TOKEN_MANDATORY_LABEL tml = {0};
321 ok = ::ConvertStringSidToSid(SDDL_ML_LOW, &integrity_sid);
322 if (!ok) {
323 DLOG(ERROR) << "::ConvertStringSidToSid failed: " << GetLastError();
324 return ok;
325 }
326
327 tml.Label.Attributes = SE_GROUP_INTEGRITY | SE_GROUP_INTEGRITY_ENABLED;
328 tml.Label.Sid = integrity_sid;
329 ok = ::SetTokenInformation(impersonation_token, TokenIntegrityLevel,
330 &tml, sizeof(tml) + ::GetLengthSid(integrity_sid));
331 ::LocalFree(integrity_sid);
332 if (!ok) {
333 DLOG(ERROR) << "::SetTokenInformation failed: " << GetLastError();
334 return ok;
335 }
336
337 // Switch current thread to low integrity.
338 ok = ::ImpersonateLoggedOnUser(impersonation_token);
339 if (ok) {
340 impersonated_ = true;
341 } else {
342 DLOG(ERROR) << "::ImpersonateLoggedOnUser failed: " << GetLastError();
343 }
344
345 return ok;
346}
347
348bool LowIntegrityToken::IsImpersonated() {
349 HANDLE token = NULL;
350 if (!::OpenThreadToken(::GetCurrentThread(), 0, false, &token) &&
351 ::GetLastError() != ERROR_NO_TOKEN) {
352 return true;
353 }
354
355 if (token)
356 ::CloseHandle(token);
357
358 return false;
359}
360
[email protected]7bc272f2009-12-09 01:09:28361HRESULT LaunchIEAsComServer(IWebBrowser2** web_browser) {
362 if (!web_browser)
363 return E_INVALIDARG;
364
[email protected]7aa8d972010-02-17 04:24:49365 AllowSetForegroundWindow(ASFW_ANY);
366
[email protected]7bc272f2009-12-09 01:09:28367 HRESULT hr = S_OK;
368 DWORD cocreate_flags = CLSCTX_LOCAL_SERVER;
369 chrome_frame_test::LowIntegrityToken token;
370 // Vista has a bug which manifests itself when a medium integrity process
371 // launches a COM server like IE which runs in protected mode due to UAC.
372 // This causes the IWebBrowser2 interface which is returned to be useless,
373 // i.e it does not receive any events, etc. Our workaround for this is
374 // to impersonate a low integrity token and then launch IE.
[email protected]935aa542010-10-15 01:59:15375 if (base::win::GetVersion() == base::win::VERSION_VISTA &&
[email protected]6448b3ab2010-07-12 22:07:03376 GetInstalledIEVersion() == IE_7) {
[email protected]7bc272f2009-12-09 01:09:28377 // Create medium integrity browser that will launch IE broker.
378 ScopedComPtr<IWebBrowser2> medium_integrity_browser;
379 hr = medium_integrity_browser.CreateInstance(CLSID_InternetExplorer, NULL,
380 CLSCTX_LOCAL_SERVER);
381 if (FAILED(hr))
382 return hr;
383 medium_integrity_browser->Quit();
384 // Broker remains alive.
385 if (!token.Impersonate()) {
386 hr = HRESULT_FROM_WIN32(GetLastError());
387 return hr;
388 }
389
390 cocreate_flags |= CLSCTX_ENABLE_CLOAKING;
391 }
392
393 hr = ::CoCreateInstance(CLSID_InternetExplorer, NULL,
394 cocreate_flags, IID_IWebBrowser2,
395 reinterpret_cast<void**>(web_browser));
396 // ~LowIntegrityToken() will switch integrity back to medium.
397 return hr;
398}
399
[email protected]bc73b4e52010-03-26 04:16:20400FilePath GetProfilePath(const std::wstring& profile_name) {
401 FilePath profile_path;
402 chrome::GetChromeFrameUserDataDirectory(&profile_path);
403 return profile_path.Append(profile_name);
404}
405
[email protected]1da35172010-04-22 02:30:03406std::wstring GetExeVersion(const std::wstring& exe_path) {
407 scoped_ptr<FileVersionInfo> ie_version_info(
408 FileVersionInfo::CreateFileVersionInfo(FilePath(exe_path)));
409 return ie_version_info->product_version();
410}
411
412IEVersion GetInstalledIEVersion() {
413 std::wstring path = chrome_frame_test::GetExecutableAppPath(kIEImageName);
414 std::wstring version = GetExeVersion(path);
415
416 switch (version[0]) {
417 case '6':
418 return IE_6;
419 case '7':
420 return IE_7;
421 case '8':
422 return IE_8;
[email protected]d8e13512010-09-22 17:02:58423 case '9':
424 return IE_9;
[email protected]1da35172010-04-22 02:30:03425 default:
426 break;
427 }
428
429 return IE_UNSUPPORTED;
430}
431
[email protected]d8b83b212010-04-07 18:27:52432FilePath GetProfilePathForIE() {
433 FilePath profile_path;
434 // Browsers without IDeleteBrowsingHistory in non-priv mode
435 // have their profiles moved into "Temporary Internet Files".
436 // The code below basically retrieves the version of IE and computes
437 // the profile directory accordingly.
[email protected]d8e13512010-09-22 17:02:58438 if (GetInstalledIEVersion() >= IE_8) {
[email protected]d8b83b212010-04-07 18:27:52439 profile_path = GetProfilePath(kIEProfileName);
440 } else {
441 profile_path = GetIETemporaryFilesFolder();
442 profile_path = profile_path.Append(L"Google Chrome Frame");
443 }
444 return profile_path;
445}
446
[email protected]6448b3ab2010-07-12 22:07:03447FilePath GetTestDataFolder() {
448 FilePath test_dir;
449 PathService::Get(base::DIR_SOURCE_ROOT, &test_dir);
450 test_dir = test_dir.Append(FILE_PATH_LITERAL("chrome_frame"))
451 .Append(FILE_PATH_LITERAL("test"))
452 .Append(FILE_PATH_LITERAL("data"));
453 return test_dir;
454}
455
456std::wstring GetPathFromUrl(const std::wstring& url) {
457 string16 url16 = WideToUTF16(url);
458 GURL gurl = GURL(url16);
459 if (gurl.has_query()) {
460 GURL::Replacements replacements;
461 replacements.ClearQuery();
462 gurl = gurl.ReplaceComponents(replacements);
463 }
464 return UTF8ToWide(gurl.PathForRequest());
465}
466
467std::wstring GetPathAndQueryFromUrl(const std::wstring& url) {
468 string16 url16 = WideToUTF16(url);
469 GURL gurl = GURL(url16);
470 return UTF8ToWide(gurl.PathForRequest());
471}
472
473bool AddCFMetaTag(std::string* html_data) {
474 if (!html_data) {
475 NOTREACHED();
476 return false;
477 }
[email protected]59aa617b2010-09-02 22:02:09478 std::string lower = StringToLowerASCII(*html_data);
479 size_t head = lower.find("<head>");
[email protected]6448b3ab2010-07-12 22:07:03480 if (head == std::string::npos) {
481 // Add missing head section.
[email protected]59aa617b2010-09-02 22:02:09482 size_t html = lower.find("<html>");
[email protected]6448b3ab2010-07-12 22:07:03483 if (html != std::string::npos) {
[email protected]59aa617b2010-09-02 22:02:09484 head = html + strlen("<html>");
485 html_data->insert(head, "<head></head>");
486 } else {
487 DLOG(ERROR) << "Meta tag will not be injected "
488 << "because the html tag could not be found";
[email protected]6448b3ab2010-07-12 22:07:03489 }
490 }
491 if (head != std::string::npos) {
492 html_data->insert(
493 head + strlen("<head>"),
494 "<meta http-equiv=\"x-ua-compatible\" content=\"chrome=1\" />");
495 }
496 return head != std::string::npos;
497}
498
[email protected]5d91c9e2010-07-28 17:25:28499CloseIeAtEndOfScope::~CloseIeAtEndOfScope() {
500 int closed = CloseAllIEWindows();
501 DLOG_IF(ERROR, closed != 0) << "Closed " << closed << " windows forcefully";
502}
503
[email protected]2014db002010-09-28 13:46:10504// Attempt to connect to a running crash_service instance. Success occurs if we
505// can actually connect to the service's pipe or we receive ERROR_PIPE_BUSY.
506// Waits up to |timeout_ms| for success. |timeout_ms| may be 0, meaning only try
507// once, or negative, meaning wait forever.
508bool DetectRunningCrashService(int timeout_ms) {
509 // Wait for the crash_service.exe to be ready for clients.
510 base::Time start = base::Time::Now();
511 ScopedHandle new_pipe;
512
513 while (true) {
514 new_pipe.Set(::CreateFile(kCrashServicePipeName,
515 kCrashServicePipeDesiredAccess,
516 0, // dwShareMode
517 NULL, // lpSecurityAttributes
518 OPEN_EXISTING,
519 kCrashServicePipeFlagsAndAttributes,
520 NULL)); // hTemplateFile
521
522 if (new_pipe.IsValid()) {
523 return true;
524 }
525
526 switch (::GetLastError()) {
527 case ERROR_PIPE_BUSY:
528 // OK, it exists, let's assume that clients will eventually be able to
529 // connect to it.
530 return true;
531 case ERROR_FILE_NOT_FOUND:
532 // Wait a bit longer
533 break;
534 default:
535 DLOG(WARNING) << "Unexpected error while checking crash_service.exe's "
536 << "pipe: " << win_util::FormatLastWin32Error();
537 // Go ahead and wait in case it clears up.
538 break;
539 }
540
541 if (timeout_ms == 0) {
542 return false;
543 } else if (timeout_ms > 0) {
544 base::TimeDelta duration = base::Time::Now() - start;
545 if (duration.InMilliseconds() > timeout_ms) {
546 return false;
547 }
548 }
549
550 Sleep(10);
551 }
552}
553
[email protected]d9f92192010-06-23 14:51:32554base::ProcessHandle StartCrashService() {
[email protected]2014db002010-09-28 13:46:10555 if (DetectRunningCrashService(kCrashServiceStartupTimeoutMs)) {
556 DLOG(INFO) << "crash_service.exe is already running. We will use the "
557 << "existing process and leave it running after tests complete.";
558 return NULL;
559 }
560
[email protected]d9f92192010-06-23 14:51:32561 FilePath exe_dir;
562 if (!PathService::Get(base::DIR_EXE, &exe_dir)) {
563 DCHECK(false);
564 return NULL;
565 }
566
567 base::ProcessHandle crash_service = NULL;
568
[email protected]2014db002010-09-28 13:46:10569 DLOG(INFO) << "Starting crash_service.exe so you know if a test crashes!";
570
[email protected]d9f92192010-06-23 14:51:32571 FilePath crash_service_path = exe_dir.AppendASCII("crash_service.exe");
572 if (!base::LaunchApp(crash_service_path.value(), false, false,
573 &crash_service)) {
574 DLOG(ERROR) << "Couldn't start crash_service.exe";
575 return NULL;
576 }
577
[email protected]2014db002010-09-28 13:46:10578 base::Time start = base::Time::Now();
579
580 if (DetectRunningCrashService(kCrashServiceStartupTimeoutMs)) {
581 DLOG(INFO) << "crash_service.exe is ready for clients in "
582 << (base::Time::Now() - start).InMilliseconds() << "ms.";
583 return crash_service;
584 } else {
585 DLOG(ERROR) << "crash_service.exe failed to accept client connections "
586 << "within " << kCrashServiceStartupTimeoutMs << "ms. "
587 << "Terminating it now.";
588
589 // First check to see if it's even still running just to minimize the
590 // likelihood of spurious error messages from KillProcess.
591 if (WAIT_OBJECT_0 != ::WaitForSingleObject(crash_service, 0)) {
592 base::KillProcess(crash_service, 0, false);
593 }
594 return NULL;
595 }
[email protected]d9f92192010-06-23 14:51:32596}
597
[email protected]f7817822009-09-24 05:11:58598} // namespace chrome_frame_test