| Index: chrome_frame/test/chrome_frame_test_utils.cc
|
| ===================================================================
|
| --- chrome_frame/test/chrome_frame_test_utils.cc (revision 0)
|
| +++ chrome_frame/test/chrome_frame_test_utils.cc (revision 0)
|
| @@ -0,0 +1,416 @@
|
| +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chrome_frame/test/chrome_frame_test_utils.h"
|
| +
|
| +#include <atlbase.h>
|
| +#include <atlwin.h>
|
| +#include <iepmapi.h>
|
| +
|
| +#include "base/registry.h" // to find IE and firefox
|
| +#include "base/scoped_handle.h"
|
| +#include "base/scoped_comptr_win.h"
|
| +#include "base/string_util.h"
|
| +#include "base/win_util.h"
|
| +#include "chrome/common/chrome_switches.h"
|
| +
|
| +namespace chrome_frame_test {
|
| +
|
| +const wchar_t kIEImageName[] = L"iexplore.exe";
|
| +const wchar_t kIEBrokerImageName[] = L"ieuser.exe";
|
| +const wchar_t kFirefoxImageName[] = L"firefox.exe";
|
| +const wchar_t kOperaImageName[] = L"opera.exe";
|
| +const wchar_t kSafariImageName[] = L"safari.exe";
|
| +const wchar_t kChromeImageName[] = L"chrome.exe";
|
| +
|
| +bool IsTopLevelWindow(HWND window) {
|
| + long style = GetWindowLong(window, GWL_STYLE); // NOLINT
|
| + if (!(style & WS_CHILD))
|
| + return true;
|
| +
|
| + HWND parent = GetParent(window);
|
| + if (!parent)
|
| + return true;
|
| +
|
| + if (parent == GetDesktopWindow())
|
| + return true;
|
| +
|
| + return false;
|
| +}
|
| +
|
| +// Callback function for EnumThreadWindows.
|
| +BOOL CALLBACK CloseWindowsThreadCallback(HWND hwnd, LPARAM param) {
|
| + int& count = *reinterpret_cast<int*>(param);
|
| + if (IsWindowVisible(hwnd)) {
|
| + if (IsWindowEnabled(hwnd)) {
|
| + DWORD results = 0;
|
| + if (!::SendMessageTimeout(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0, SMTO_BLOCK,
|
| + 10000, &results)) {
|
| + DLOG(WARNING) << "Window hung: " << StringPrintf(L"%08X", hwnd);
|
| + }
|
| + count++;
|
| + } else {
|
| + DLOG(WARNING) << "Skipping disabled window: "
|
| + << StringPrintf(L"%08X", hwnd);
|
| + }
|
| + }
|
| + return TRUE; // continue enumeration
|
| +}
|
| +
|
| +// Attempts to close all non-child, visible windows on the given thread.
|
| +// The return value is the number of visible windows a close request was
|
| +// sent to.
|
| +int CloseVisibleTopLevelWindowsOnThread(DWORD thread_id) {
|
| + int window_close_attempts = 0;
|
| + EnumThreadWindows(thread_id, CloseWindowsThreadCallback,
|
| + reinterpret_cast<LPARAM>(&window_close_attempts));
|
| + return window_close_attempts;
|
| +}
|
| +
|
| +// Enumerates the threads of a process and attempts to close visible non-child
|
| +// windows on all threads of the process.
|
| +// The return value is the number of visible windows a close request was
|
| +// sent to.
|
| +int CloseVisibleWindowsOnAllThreads(HANDLE process) {
|
| + DWORD process_id = ::GetProcessId(process);
|
| + if (process_id == 0) {
|
| + NOTREACHED();
|
| + return 0;
|
| + }
|
| +
|
| + ScopedHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0));
|
| + if (!snapshot.IsValid()) {
|
| + NOTREACHED();
|
| + return 0;
|
| + }
|
| +
|
| + int window_close_attempts = 0;
|
| + THREADENTRY32 te = { sizeof(THREADENTRY32) };
|
| + if (Thread32First(snapshot, &te)) {
|
| + do {
|
| + if (RTL_CONTAINS_FIELD(&te, te.dwSize, th32OwnerProcessID) &&
|
| + te.th32OwnerProcessID == process_id) {
|
| + window_close_attempts +=
|
| + CloseVisibleTopLevelWindowsOnThread(te.th32ThreadID);
|
| + }
|
| + te.dwSize = sizeof(te);
|
| + } while (Thread32Next(snapshot, &te));
|
| + }
|
| +
|
| + return window_close_attempts;
|
| +}
|
| +
|
| +class ForegroundHelperWindow : public CWindowImpl<ForegroundHelperWindow> {
|
| + public:
|
| +BEGIN_MSG_MAP(ForegroundHelperWindow)
|
| + MESSAGE_HANDLER(WM_HOTKEY, OnHotKey)
|
| +END_MSG_MAP()
|
| +
|
| + HRESULT SetForeground(HWND window) {
|
| + DCHECK(::IsWindow(window));
|
| + if (NULL == Create(NULL, NULL, NULL, WS_POPUP))
|
| + return AtlHresultFromLastError();
|
| +
|
| + static const int hotkey_id = 0x0000baba;
|
| +
|
| + SetWindowLongPtr(GWLP_USERDATA, reinterpret_cast<ULONG_PTR>(window));
|
| + RegisterHotKey(m_hWnd, hotkey_id, 0, VK_F22);
|
| +
|
| + MSG msg = {0};
|
| + PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
|
| +
|
| + INPUT hotkey = {0};
|
| + hotkey.type = INPUT_KEYBOARD;
|
| + hotkey.ki.wVk = VK_F22;
|
| + SendInput(1, &hotkey, sizeof(hotkey));
|
| +
|
| + while (GetMessage(&msg, NULL, 0, 0)) {
|
| + TranslateMessage(&msg);
|
| + DispatchMessage(&msg);
|
| + if (WM_HOTKEY == msg.message)
|
| + break;
|
| + }
|
| +
|
| + UnregisterHotKey(m_hWnd, hotkey_id);
|
| + DestroyWindow();
|
| +
|
| + return S_OK;
|
| + }
|
| +
|
| + LRESULT OnHotKey(UINT msg, WPARAM wp, LPARAM lp, BOOL& handled) { // NOLINT
|
| + HWND window = reinterpret_cast<HWND>(GetWindowLongPtr(GWLP_USERDATA));
|
| + SetForegroundWindow(window);
|
| + return 1;
|
| + }
|
| +};
|
| +
|
| +bool ForceSetForegroundWindow(HWND window) {
|
| + if (GetForegroundWindow() == window)
|
| + return true;
|
| + ForegroundHelperWindow foreground_helper_window;
|
| + HRESULT hr = foreground_helper_window.SetForeground(window);
|
| + return SUCCEEDED(hr);
|
| +}
|
| +
|
| +struct PidAndWindow {
|
| + base::ProcessId pid;
|
| + HWND hwnd;
|
| +};
|
| +
|
| +BOOL CALLBACK FindWindowInProcessCallback(HWND hwnd, LPARAM param) {
|
| + PidAndWindow* paw = reinterpret_cast<PidAndWindow*>(param);
|
| + base::ProcessId pid;
|
| + GetWindowThreadProcessId(hwnd, &pid);
|
| + if (pid == paw->pid && IsWindowVisible(hwnd)) {
|
| + paw->hwnd = hwnd;
|
| + return FALSE;
|
| + }
|
| +
|
| + return TRUE;
|
| +}
|
| +
|
| +bool EnsureProcessInForeground(base::ProcessId process_id) {
|
| + HWND hwnd = GetForegroundWindow();
|
| + base::ProcessId current_foreground_pid = 0;
|
| + DWORD active_thread_id = GetWindowThreadProcessId(hwnd,
|
| + ¤t_foreground_pid);
|
| + if (current_foreground_pid == process_id)
|
| + return true;
|
| +
|
| + PidAndWindow paw = { process_id };
|
| + EnumWindows(FindWindowInProcessCallback, reinterpret_cast<LPARAM>(&paw));
|
| + if (!IsWindow(paw.hwnd)) {
|
| + DLOG(ERROR) << "failed to find process window";
|
| + return false;
|
| + }
|
| +
|
| + bool ret = ForceSetForegroundWindow(paw.hwnd);
|
| + DLOG_IF(ERROR, !ret) << "ForceSetForegroundWindow: " << ret;
|
| +
|
| + return ret;
|
| +}
|
| +
|
| +// Iterates through all the characters in the string and simulates
|
| +// keyboard input. The input goes to the currently active application.
|
| +bool SendString(const wchar_t* string) {
|
| + DCHECK(string != NULL);
|
| +
|
| + INPUT input[2] = {0};
|
| + input[0].type = INPUT_KEYBOARD;
|
| + input[0].ki.dwFlags = KEYEVENTF_UNICODE; // to avoid shift, etc.
|
| + input[1] = input[0];
|
| + input[1].ki.dwFlags |= KEYEVENTF_KEYUP;
|
| +
|
| + for (const wchar_t* p = string; *p; p++) {
|
| + input[0].ki.wScan = input[1].ki.wScan = *p;
|
| + SendInput(2, input, sizeof(INPUT));
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void SendVirtualKey(int16 key) {
|
| + INPUT input = { INPUT_KEYBOARD };
|
| + input.ki.wVk = key;
|
| + SendInput(1, &input, sizeof(input));
|
| + input.ki.dwFlags = KEYEVENTF_KEYUP;
|
| + SendInput(1, &input, sizeof(input));
|
| +}
|
| +
|
| +void SendChar(char c) {
|
| + SendVirtualKey(VkKeyScanA(c));
|
| +}
|
| +
|
| +void SendString(const char* s) {
|
| + while (*s) {
|
| + SendChar(*s);
|
| + s++;
|
| + }
|
| +}
|
| +
|
| +// Sends a keystroke to the currently active application with optional
|
| +// modifiers set.
|
| +bool SendMnemonic(WORD mnemonic_char, bool shift_pressed, bool control_pressed,
|
| + bool alt_pressed) {
|
| + INPUT special_keys[3] = {0};
|
| + for (int index = 0; index < arraysize(special_keys); ++index) {
|
| + special_keys[index].type = INPUT_KEYBOARD;
|
| + special_keys[index].ki.dwFlags = 0;
|
| + }
|
| +
|
| + int num_special_keys = 0;
|
| + if (shift_pressed) {
|
| + special_keys[num_special_keys].ki.wVk = VK_SHIFT;
|
| + num_special_keys++;
|
| + }
|
| +
|
| + if (control_pressed) {
|
| + special_keys[num_special_keys].ki.wVk = VK_CONTROL;
|
| + num_special_keys++;
|
| + }
|
| +
|
| + if (alt_pressed) {
|
| + special_keys[num_special_keys].ki.wVk = VK_MENU;
|
| + num_special_keys++;
|
| + }
|
| +
|
| + // Depress the modifiers.
|
| + SendInput(num_special_keys, special_keys, sizeof(INPUT));
|
| +
|
| + Sleep(100);
|
| +
|
| + INPUT mnemonic = {0};
|
| + mnemonic.type = INPUT_KEYBOARD;
|
| + mnemonic.ki.wVk = mnemonic_char;
|
| +
|
| + // Depress and release the mnemonic.
|
| + SendInput(1, &mnemonic, sizeof(INPUT));
|
| + Sleep(100);
|
| +
|
| + mnemonic.ki.dwFlags |= KEYEVENTF_KEYUP;
|
| + SendInput(1, &mnemonic, sizeof(INPUT));
|
| + Sleep(100);
|
| +
|
| + // Now release the modifiers.
|
| + for (int index = 0; index < num_special_keys; index++) {
|
| + special_keys[index].ki.dwFlags |= KEYEVENTF_KEYUP;
|
| + }
|
| +
|
| + SendInput(num_special_keys, special_keys, sizeof(INPUT));
|
| + Sleep(100);
|
| +
|
| + return true;
|
| +}
|
| +
|
| +std::wstring GetExecutableAppPath(const std::wstring& file) {
|
| + std::wstring kAppPathsKey =
|
| + L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\";
|
| +
|
| + std::wstring app_path;
|
| + RegKey key(HKEY_LOCAL_MACHINE, (kAppPathsKey + file).c_str());
|
| + if (key.Handle()) {
|
| + key.ReadValue(NULL, &app_path);
|
| + }
|
| +
|
| + return app_path;
|
| +}
|
| +
|
| +std::wstring FormatCommandForApp(const std::wstring& exe_name,
|
| + const std::wstring& argument) {
|
| + std::wstring reg_path(StringPrintf(L"Applications\\%ls\\shell\\open\\command",
|
| + exe_name.c_str()));
|
| + RegKey key(HKEY_CLASSES_ROOT, reg_path.c_str());
|
| +
|
| + std::wstring command;
|
| + if (key.Handle()) {
|
| + key.ReadValue(NULL, &command);
|
| + int found = command.find(L"%1");
|
| + if (found >= 0) {
|
| + command.replace(found, 2, argument);
|
| + }
|
| + }
|
| + return command;
|
| +}
|
| +
|
| +base::ProcessHandle LaunchExecutable(const std::wstring& executable,
|
| + const std::wstring& argument) {
|
| + base::ProcessHandle process = NULL;
|
| + std::wstring path = GetExecutableAppPath(executable);
|
| + if (path.empty()) {
|
| + path = FormatCommandForApp(executable, argument);
|
| + if (path.empty()) {
|
| + DLOG(ERROR) << "Failed to find executable: " << executable;
|
| + } else {
|
| + CommandLine cmdline(L"");
|
| + cmdline.ParseFromString(path);
|
| + base::LaunchApp(cmdline, false, false, &process);
|
| + }
|
| + } else {
|
| + CommandLine cmdline(path);
|
| + cmdline.AppendLooseValue(argument);
|
| + base::LaunchApp(cmdline, false, false, &process);
|
| + }
|
| + return process;
|
| +}
|
| +
|
| +base::ProcessHandle LaunchFirefox(const std::wstring& url) {
|
| + return LaunchExecutable(kFirefoxImageName, url);
|
| +}
|
| +
|
| +base::ProcessHandle LaunchSafari(const std::wstring& url) {
|
| + return LaunchExecutable(kSafariImageName, url);
|
| +}
|
| +
|
| +base::ProcessHandle LaunchChrome(const std::wstring& url) {
|
| + return LaunchExecutable(kChromeImageName,
|
| + StringPrintf(L"--%ls ", switches::kNoFirstRun) + url);
|
| +}
|
| +
|
| +base::ProcessHandle LaunchOpera(const std::wstring& url) {
|
| + // NOTE: For Opera tests to work it must be configured to start up with
|
| + // a blank page. There is an command line switch, -nosession, that's supposed
|
| + // to avoid opening up the previous session, but that switch is not working.
|
| + // TODO(tommi): Include a special ini file (opera6.ini) for opera and launch
|
| + // with our required settings. This file is by default stored here:
|
| + // "%USERPROFILE%\Application Data\Opera\Opera\profile\opera6.ini"
|
| + return LaunchExecutable(kOperaImageName, url);
|
| +}
|
| +
|
| +base::ProcessHandle LaunchIEOnVista(const std::wstring& url) {
|
| + typedef HRESULT (WINAPI* IELaunchURLPtr)(
|
| + const wchar_t* url,
|
| + PROCESS_INFORMATION *pi,
|
| + VOID *info);
|
| +
|
| + IELaunchURLPtr launch;
|
| + PROCESS_INFORMATION pi = {0};
|
| + IELAUNCHURLINFO info = {sizeof info, 0};
|
| + HMODULE h = LoadLibrary(L"ieframe.dll");
|
| + if (!h)
|
| + return NULL;
|
| + launch = reinterpret_cast<IELaunchURLPtr>(GetProcAddress(h, "IELaunchURL"));
|
| + HRESULT hr = launch(url.c_str(), &pi, &info);
|
| + FreeLibrary(h);
|
| + if (SUCCEEDED(hr))
|
| + CloseHandle(pi.hThread);
|
| + return pi.hProcess;
|
| +}
|
| +
|
| +base::ProcessHandle LaunchIE(const std::wstring& url) {
|
| + if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA) {
|
| + return LaunchIEOnVista(url);
|
| + } else {
|
| + return LaunchExecutable(kIEImageName, url);
|
| + }
|
| +}
|
| +
|
| +int CloseAllIEWindows() {
|
| + int ret = 0;
|
| +
|
| + ScopedComPtr<IShellWindows> windows;
|
| + HRESULT hr = ::CoCreateInstance(__uuidof(ShellWindows), NULL, CLSCTX_ALL,
|
| + IID_IShellWindows, reinterpret_cast<void**>(windows.Receive()));
|
| + DCHECK(SUCCEEDED(hr));
|
| +
|
| + if (SUCCEEDED(hr)) {
|
| + long count = 0; // NOLINT
|
| + windows->get_Count(&count);
|
| + VARIANT i = { VT_I4 };
|
| + for (i.lVal = 0; i.lVal < count; ++i.lVal) {
|
| + ScopedComPtr<IDispatch> folder;
|
| + windows->Item(i, folder.Receive());
|
| + if (folder != NULL) {
|
| + ScopedComPtr<IWebBrowser2> browser;
|
| + if (SUCCEEDED(browser.QueryFrom(folder))) {
|
| + browser->Quit();
|
| + ++ret;
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + return ret;
|
| +}
|
| +
|
| +} // namespace chrome_frame_test
|
|
|