OLD | NEW |
| (Empty) |
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. | |
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. | |
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. | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
310 LowIntegrityToken::LowIntegrityToken() : impersonated_(false) { | |
311 } | |
312 | |
313 LowIntegrityToken::~LowIntegrityToken() { | |
314 RevertToSelf(); | |
315 } | |
316 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
514 std::wstring GetPathFromUrl(const std::wstring& url) { | |
515 base::string16 url16 = base::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 base::UTF8ToWide(gurl.PathForRequest()); | |
523 } | |
524 | |
525 std::wstring GetPathAndQueryFromUrl(const std::wstring& url) { | |
526 base::string16 url16 = base::WideToUTF16(url); | |
527 GURL gurl = GURL(url16); | |
528 return base::UTF8ToWide(gurl.PathForRequest()); | |
529 } | |
530 | |
531 std::wstring GetClipboardText() { | |
532 base::string16 text16; | |
533 ui::Clipboard::GetForCurrentThread()->ReadText( | |
534 ui::CLIPBOARD_TYPE_COPY_PASTE, &text16); | |
535 return base::UTF16ToWide(text16); | |
536 } | |
537 | |
538 void DestroyClipboard() { | |
539 ui::Clipboard::DestroyClipboardForCurrentThread(); | |
540 } | |
541 | |
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(base::WideToUTF16(text)); | |
547 } | |
548 | |
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 | |
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. | |
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 | |
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 | |
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 | |
679 ScopedVirtualizeHklmAndHkcu::~ScopedVirtualizeHklmAndHkcu() { | |
680 } | |
681 | |
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 | |
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 | |
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 | |
711 std::string GetLocalIPv4Address() { | |
712 std::string address; | |
713 net::NetworkInterfaceList nic_list; | |
714 | |
715 if (!net::GetNetworkList(&nic_list, | |
716 net::INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES)) { | |
717 LOG(ERROR) << "GetNetworkList failed to look up non-loopback adapters. " | |
718 << "Tests will be run over the loopback adapter, which may " | |
719 << "result in hangs."; | |
720 } else { | |
721 // GetNetworkList only returns 'Up' non-loopback adapters. Select the first | |
722 // IPv4 address found - we should be able to bind/connect over it. | |
723 for (size_t i = 0; i < nic_list.size(); ++i) { | |
724 if (nic_list[i].address.size() != net::kIPv4AddressSize) | |
725 continue; | |
726 char* address_string = | |
727 inet_ntoa(*reinterpret_cast<in_addr*>(&nic_list[i].address[0])); | |
728 DCHECK(address_string != NULL); | |
729 if (address_string != NULL) { | |
730 LOG(INFO) << "HTTP tests will run over " << address_string << "."; | |
731 address.assign(address_string); | |
732 break; | |
733 } | |
734 } | |
735 } | |
736 | |
737 if (address.empty()) { | |
738 LOG(ERROR) << "Failed to find a non-loopback IP_V4 address. Tests will be " | |
739 << "run over the loopback adapter, which may result in hangs."; | |
740 address.assign("127.0.0.1"); | |
741 } | |
742 | |
743 return address; | |
744 } | |
745 | |
746 } // namespace chrome_frame_test | |
OLD | NEW |