OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2006-2008 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 <atlbase.h> |
| 8 #include <atlwin.h> |
| 9 #include <iepmapi.h> |
| 10 |
| 11 #include "base/registry.h" // to find IE and firefox |
| 12 #include "base/scoped_handle.h" |
| 13 #include "base/scoped_comptr_win.h" |
| 14 #include "base/string_util.h" |
| 15 #include "base/win_util.h" |
| 16 #include "chrome/common/chrome_switches.h" |
| 17 |
| 18 namespace chrome_frame_test { |
| 19 |
| 20 const wchar_t kIEImageName[] = L"iexplore.exe"; |
| 21 const wchar_t kIEBrokerImageName[] = L"ieuser.exe"; |
| 22 const wchar_t kFirefoxImageName[] = L"firefox.exe"; |
| 23 const wchar_t kOperaImageName[] = L"opera.exe"; |
| 24 const wchar_t kSafariImageName[] = L"safari.exe"; |
| 25 const wchar_t kChromeImageName[] = L"chrome.exe"; |
| 26 |
| 27 bool IsTopLevelWindow(HWND window) { |
| 28 long style = GetWindowLong(window, GWL_STYLE); // NOLINT |
| 29 if (!(style & WS_CHILD)) |
| 30 return true; |
| 31 |
| 32 HWND parent = GetParent(window); |
| 33 if (!parent) |
| 34 return true; |
| 35 |
| 36 if (parent == GetDesktopWindow()) |
| 37 return true; |
| 38 |
| 39 return false; |
| 40 } |
| 41 |
| 42 // Callback function for EnumThreadWindows. |
| 43 BOOL CALLBACK CloseWindowsThreadCallback(HWND hwnd, LPARAM param) { |
| 44 int& count = *reinterpret_cast<int*>(param); |
| 45 if (IsWindowVisible(hwnd)) { |
| 46 if (IsWindowEnabled(hwnd)) { |
| 47 DWORD results = 0; |
| 48 if (!::SendMessageTimeout(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0, SMTO_BLOCK, |
| 49 10000, &results)) { |
| 50 DLOG(WARNING) << "Window hung: " << StringPrintf(L"%08X", hwnd); |
| 51 } |
| 52 count++; |
| 53 } else { |
| 54 DLOG(WARNING) << "Skipping disabled window: " |
| 55 << StringPrintf(L"%08X", hwnd); |
| 56 } |
| 57 } |
| 58 return TRUE; // continue enumeration |
| 59 } |
| 60 |
| 61 // Attempts to close all non-child, visible windows on the given thread. |
| 62 // The return value is the number of visible windows a close request was |
| 63 // sent to. |
| 64 int CloseVisibleTopLevelWindowsOnThread(DWORD thread_id) { |
| 65 int window_close_attempts = 0; |
| 66 EnumThreadWindows(thread_id, CloseWindowsThreadCallback, |
| 67 reinterpret_cast<LPARAM>(&window_close_attempts)); |
| 68 return window_close_attempts; |
| 69 } |
| 70 |
| 71 // Enumerates the threads of a process and attempts to close visible non-child |
| 72 // windows on all threads of the process. |
| 73 // The return value is the number of visible windows a close request was |
| 74 // sent to. |
| 75 int CloseVisibleWindowsOnAllThreads(HANDLE process) { |
| 76 DWORD process_id = ::GetProcessId(process); |
| 77 if (process_id == 0) { |
| 78 NOTREACHED(); |
| 79 return 0; |
| 80 } |
| 81 |
| 82 ScopedHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0)); |
| 83 if (!snapshot.IsValid()) { |
| 84 NOTREACHED(); |
| 85 return 0; |
| 86 } |
| 87 |
| 88 int window_close_attempts = 0; |
| 89 THREADENTRY32 te = { sizeof(THREADENTRY32) }; |
| 90 if (Thread32First(snapshot, &te)) { |
| 91 do { |
| 92 if (RTL_CONTAINS_FIELD(&te, te.dwSize, th32OwnerProcessID) && |
| 93 te.th32OwnerProcessID == process_id) { |
| 94 window_close_attempts += |
| 95 CloseVisibleTopLevelWindowsOnThread(te.th32ThreadID); |
| 96 } |
| 97 te.dwSize = sizeof(te); |
| 98 } while (Thread32Next(snapshot, &te)); |
| 99 } |
| 100 |
| 101 return window_close_attempts; |
| 102 } |
| 103 |
| 104 class ForegroundHelperWindow : public CWindowImpl<ForegroundHelperWindow> { |
| 105 public: |
| 106 BEGIN_MSG_MAP(ForegroundHelperWindow) |
| 107 MESSAGE_HANDLER(WM_HOTKEY, OnHotKey) |
| 108 END_MSG_MAP() |
| 109 |
| 110 HRESULT SetForeground(HWND window) { |
| 111 DCHECK(::IsWindow(window)); |
| 112 if (NULL == Create(NULL, NULL, NULL, WS_POPUP)) |
| 113 return AtlHresultFromLastError(); |
| 114 |
| 115 static const int hotkey_id = 0x0000baba; |
| 116 |
| 117 SetWindowLongPtr(GWLP_USERDATA, reinterpret_cast<ULONG_PTR>(window)); |
| 118 RegisterHotKey(m_hWnd, hotkey_id, 0, VK_F22); |
| 119 |
| 120 MSG msg = {0}; |
| 121 PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); |
| 122 |
| 123 INPUT hotkey = {0}; |
| 124 hotkey.type = INPUT_KEYBOARD; |
| 125 hotkey.ki.wVk = VK_F22; |
| 126 SendInput(1, &hotkey, sizeof(hotkey)); |
| 127 |
| 128 while (GetMessage(&msg, NULL, 0, 0)) { |
| 129 TranslateMessage(&msg); |
| 130 DispatchMessage(&msg); |
| 131 if (WM_HOTKEY == msg.message) |
| 132 break; |
| 133 } |
| 134 |
| 135 UnregisterHotKey(m_hWnd, hotkey_id); |
| 136 DestroyWindow(); |
| 137 |
| 138 return S_OK; |
| 139 } |
| 140 |
| 141 LRESULT OnHotKey(UINT msg, WPARAM wp, LPARAM lp, BOOL& handled) { // NOLINT |
| 142 HWND window = reinterpret_cast<HWND>(GetWindowLongPtr(GWLP_USERDATA)); |
| 143 SetForegroundWindow(window); |
| 144 return 1; |
| 145 } |
| 146 }; |
| 147 |
| 148 bool ForceSetForegroundWindow(HWND window) { |
| 149 if (GetForegroundWindow() == window) |
| 150 return true; |
| 151 ForegroundHelperWindow foreground_helper_window; |
| 152 HRESULT hr = foreground_helper_window.SetForeground(window); |
| 153 return SUCCEEDED(hr); |
| 154 } |
| 155 |
| 156 struct PidAndWindow { |
| 157 base::ProcessId pid; |
| 158 HWND hwnd; |
| 159 }; |
| 160 |
| 161 BOOL CALLBACK FindWindowInProcessCallback(HWND hwnd, LPARAM param) { |
| 162 PidAndWindow* paw = reinterpret_cast<PidAndWindow*>(param); |
| 163 base::ProcessId pid; |
| 164 GetWindowThreadProcessId(hwnd, &pid); |
| 165 if (pid == paw->pid && IsWindowVisible(hwnd)) { |
| 166 paw->hwnd = hwnd; |
| 167 return FALSE; |
| 168 } |
| 169 |
| 170 return TRUE; |
| 171 } |
| 172 |
| 173 bool EnsureProcessInForeground(base::ProcessId process_id) { |
| 174 HWND hwnd = GetForegroundWindow(); |
| 175 base::ProcessId current_foreground_pid = 0; |
| 176 DWORD active_thread_id = GetWindowThreadProcessId(hwnd, |
| 177 ¤t_foreground_pid); |
| 178 if (current_foreground_pid == process_id) |
| 179 return true; |
| 180 |
| 181 PidAndWindow paw = { process_id }; |
| 182 EnumWindows(FindWindowInProcessCallback, reinterpret_cast<LPARAM>(&paw)); |
| 183 if (!IsWindow(paw.hwnd)) { |
| 184 DLOG(ERROR) << "failed to find process window"; |
| 185 return false; |
| 186 } |
| 187 |
| 188 bool ret = ForceSetForegroundWindow(paw.hwnd); |
| 189 DLOG_IF(ERROR, !ret) << "ForceSetForegroundWindow: " << ret; |
| 190 |
| 191 return ret; |
| 192 } |
| 193 |
| 194 // Iterates through all the characters in the string and simulates |
| 195 // keyboard input. The input goes to the currently active application. |
| 196 bool SendString(const wchar_t* string) { |
| 197 DCHECK(string != NULL); |
| 198 |
| 199 INPUT input[2] = {0}; |
| 200 input[0].type = INPUT_KEYBOARD; |
| 201 input[0].ki.dwFlags = KEYEVENTF_UNICODE; // to avoid shift, etc. |
| 202 input[1] = input[0]; |
| 203 input[1].ki.dwFlags |= KEYEVENTF_KEYUP; |
| 204 |
| 205 for (const wchar_t* p = string; *p; p++) { |
| 206 input[0].ki.wScan = input[1].ki.wScan = *p; |
| 207 SendInput(2, input, sizeof(INPUT)); |
| 208 } |
| 209 |
| 210 return true; |
| 211 } |
| 212 |
| 213 void SendVirtualKey(int16 key) { |
| 214 INPUT input = { INPUT_KEYBOARD }; |
| 215 input.ki.wVk = key; |
| 216 SendInput(1, &input, sizeof(input)); |
| 217 input.ki.dwFlags = KEYEVENTF_KEYUP; |
| 218 SendInput(1, &input, sizeof(input)); |
| 219 } |
| 220 |
| 221 void SendChar(char c) { |
| 222 SendVirtualKey(VkKeyScanA(c)); |
| 223 } |
| 224 |
| 225 void SendString(const char* s) { |
| 226 while (*s) { |
| 227 SendChar(*s); |
| 228 s++; |
| 229 } |
| 230 } |
| 231 |
| 232 // Sends a keystroke to the currently active application with optional |
| 233 // modifiers set. |
| 234 bool SendMnemonic(WORD mnemonic_char, bool shift_pressed, bool control_pressed, |
| 235 bool alt_pressed) { |
| 236 INPUT special_keys[3] = {0}; |
| 237 for (int index = 0; index < arraysize(special_keys); ++index) { |
| 238 special_keys[index].type = INPUT_KEYBOARD; |
| 239 special_keys[index].ki.dwFlags = 0; |
| 240 } |
| 241 |
| 242 int num_special_keys = 0; |
| 243 if (shift_pressed) { |
| 244 special_keys[num_special_keys].ki.wVk = VK_SHIFT; |
| 245 num_special_keys++; |
| 246 } |
| 247 |
| 248 if (control_pressed) { |
| 249 special_keys[num_special_keys].ki.wVk = VK_CONTROL; |
| 250 num_special_keys++; |
| 251 } |
| 252 |
| 253 if (alt_pressed) { |
| 254 special_keys[num_special_keys].ki.wVk = VK_MENU; |
| 255 num_special_keys++; |
| 256 } |
| 257 |
| 258 // Depress the modifiers. |
| 259 SendInput(num_special_keys, special_keys, sizeof(INPUT)); |
| 260 |
| 261 Sleep(100); |
| 262 |
| 263 INPUT mnemonic = {0}; |
| 264 mnemonic.type = INPUT_KEYBOARD; |
| 265 mnemonic.ki.wVk = mnemonic_char; |
| 266 |
| 267 // Depress and release the mnemonic. |
| 268 SendInput(1, &mnemonic, sizeof(INPUT)); |
| 269 Sleep(100); |
| 270 |
| 271 mnemonic.ki.dwFlags |= KEYEVENTF_KEYUP; |
| 272 SendInput(1, &mnemonic, sizeof(INPUT)); |
| 273 Sleep(100); |
| 274 |
| 275 // Now release the modifiers. |
| 276 for (int index = 0; index < num_special_keys; index++) { |
| 277 special_keys[index].ki.dwFlags |= KEYEVENTF_KEYUP; |
| 278 } |
| 279 |
| 280 SendInput(num_special_keys, special_keys, sizeof(INPUT)); |
| 281 Sleep(100); |
| 282 |
| 283 return true; |
| 284 } |
| 285 |
| 286 std::wstring GetExecutableAppPath(const std::wstring& file) { |
| 287 std::wstring kAppPathsKey = |
| 288 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"; |
| 289 |
| 290 std::wstring app_path; |
| 291 RegKey key(HKEY_LOCAL_MACHINE, (kAppPathsKey + file).c_str()); |
| 292 if (key.Handle()) { |
| 293 key.ReadValue(NULL, &app_path); |
| 294 } |
| 295 |
| 296 return app_path; |
| 297 } |
| 298 |
| 299 std::wstring FormatCommandForApp(const std::wstring& exe_name, |
| 300 const std::wstring& argument) { |
| 301 std::wstring reg_path(StringPrintf(L"Applications\\%ls\\shell\\open\\command", |
| 302 exe_name.c_str())); |
| 303 RegKey key(HKEY_CLASSES_ROOT, reg_path.c_str()); |
| 304 |
| 305 std::wstring command; |
| 306 if (key.Handle()) { |
| 307 key.ReadValue(NULL, &command); |
| 308 int found = command.find(L"%1"); |
| 309 if (found >= 0) { |
| 310 command.replace(found, 2, argument); |
| 311 } |
| 312 } |
| 313 return command; |
| 314 } |
| 315 |
| 316 base::ProcessHandle LaunchExecutable(const std::wstring& executable, |
| 317 const std::wstring& argument) { |
| 318 base::ProcessHandle process = NULL; |
| 319 std::wstring path = GetExecutableAppPath(executable); |
| 320 if (path.empty()) { |
| 321 path = FormatCommandForApp(executable, argument); |
| 322 if (path.empty()) { |
| 323 DLOG(ERROR) << "Failed to find executable: " << executable; |
| 324 } else { |
| 325 CommandLine cmdline(L""); |
| 326 cmdline.ParseFromString(path); |
| 327 base::LaunchApp(cmdline, false, false, &process); |
| 328 } |
| 329 } else { |
| 330 CommandLine cmdline(path); |
| 331 cmdline.AppendLooseValue(argument); |
| 332 base::LaunchApp(cmdline, false, false, &process); |
| 333 } |
| 334 return process; |
| 335 } |
| 336 |
| 337 base::ProcessHandle LaunchFirefox(const std::wstring& url) { |
| 338 return LaunchExecutable(kFirefoxImageName, url); |
| 339 } |
| 340 |
| 341 base::ProcessHandle LaunchSafari(const std::wstring& url) { |
| 342 return LaunchExecutable(kSafariImageName, url); |
| 343 } |
| 344 |
| 345 base::ProcessHandle LaunchChrome(const std::wstring& url) { |
| 346 return LaunchExecutable(kChromeImageName, |
| 347 StringPrintf(L"--%ls ", switches::kNoFirstRun) + url); |
| 348 } |
| 349 |
| 350 base::ProcessHandle LaunchOpera(const std::wstring& url) { |
| 351 // NOTE: For Opera tests to work it must be configured to start up with |
| 352 // a blank page. There is an command line switch, -nosession, that's supposed |
| 353 // to avoid opening up the previous session, but that switch is not working. |
| 354 // TODO(tommi): Include a special ini file (opera6.ini) for opera and launch |
| 355 // with our required settings. This file is by default stored here: |
| 356 // "%USERPROFILE%\Application Data\Opera\Opera\profile\opera6.ini" |
| 357 return LaunchExecutable(kOperaImageName, url); |
| 358 } |
| 359 |
| 360 base::ProcessHandle LaunchIEOnVista(const std::wstring& url) { |
| 361 typedef HRESULT (WINAPI* IELaunchURLPtr)( |
| 362 const wchar_t* url, |
| 363 PROCESS_INFORMATION *pi, |
| 364 VOID *info); |
| 365 |
| 366 IELaunchURLPtr launch; |
| 367 PROCESS_INFORMATION pi = {0}; |
| 368 IELAUNCHURLINFO info = {sizeof info, 0}; |
| 369 HMODULE h = LoadLibrary(L"ieframe.dll"); |
| 370 if (!h) |
| 371 return NULL; |
| 372 launch = reinterpret_cast<IELaunchURLPtr>(GetProcAddress(h, "IELaunchURL")); |
| 373 HRESULT hr = launch(url.c_str(), &pi, &info); |
| 374 FreeLibrary(h); |
| 375 if (SUCCEEDED(hr)) |
| 376 CloseHandle(pi.hThread); |
| 377 return pi.hProcess; |
| 378 } |
| 379 |
| 380 base::ProcessHandle LaunchIE(const std::wstring& url) { |
| 381 if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA) { |
| 382 return LaunchIEOnVista(url); |
| 383 } else { |
| 384 return LaunchExecutable(kIEImageName, url); |
| 385 } |
| 386 } |
| 387 |
| 388 int CloseAllIEWindows() { |
| 389 int ret = 0; |
| 390 |
| 391 ScopedComPtr<IShellWindows> windows; |
| 392 HRESULT hr = ::CoCreateInstance(__uuidof(ShellWindows), NULL, CLSCTX_ALL, |
| 393 IID_IShellWindows, reinterpret_cast<void**>(windows.Receive())); |
| 394 DCHECK(SUCCEEDED(hr)); |
| 395 |
| 396 if (SUCCEEDED(hr)) { |
| 397 long count = 0; // NOLINT |
| 398 windows->get_Count(&count); |
| 399 VARIANT i = { VT_I4 }; |
| 400 for (i.lVal = 0; i.lVal < count; ++i.lVal) { |
| 401 ScopedComPtr<IDispatch> folder; |
| 402 windows->Item(i, folder.Receive()); |
| 403 if (folder != NULL) { |
| 404 ScopedComPtr<IWebBrowser2> browser; |
| 405 if (SUCCEEDED(browser.QueryFrom(folder))) { |
| 406 browser->Quit(); |
| 407 ++ret; |
| 408 } |
| 409 } |
| 410 } |
| 411 } |
| 412 |
| 413 return ret; |
| 414 } |
| 415 |
| 416 } // namespace chrome_frame_test |
OLD | NEW |