summaryrefslogtreecommitdiffstats
path: root/chrome_frame/test/simulate_input.cc
diff options
context:
space:
mode:
authoramit@chromium.org <amit@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-12 18:19:07 +0000
committeramit@chromium.org <amit@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-12 18:19:07 +0000
commit4e676aa41d39aada2731e64f2807611cfad2c785 (patch)
tree409c055c2d5c1849e707e93b97f55e5afcdaf6b1 /chrome_frame/test/simulate_input.cc
parentaedd87e5b5e0ee568309eba54b31cb8aa12cd6e6 (diff)
downloadchromium_src-4e676aa41d39aada2731e64f2807611cfad2c785.zip
chromium_src-4e676aa41d39aada2731e64f2807611cfad2c785.tar.gz
chromium_src-4e676aa41d39aada2731e64f2807611cfad2c785.tar.bz2
First batch of context menu tests
Refactored various methods to send keyboard and mouse input. Fixed the context menu focus issue on IE7. Improved existing tests to make them less flaky and added 3 new tests for context menu items. BUG=34673 TEST=new tests added Review URL: http://codereview.chromium.org/604014 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@38905 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_frame/test/simulate_input.cc')
-rw-r--r--chrome_frame/test/simulate_input.cc223
1 files changed, 223 insertions, 0 deletions
diff --git a/chrome_frame/test/simulate_input.cc b/chrome_frame/test/simulate_input.cc
new file mode 100644
index 0000000..82002a9
--- /dev/null
+++ b/chrome_frame/test/simulate_input.cc
@@ -0,0 +1,223 @@
+// Copyright (c) 2010 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/simulate_input.h"
+
+#include <atlbase.h>
+#include <atlwin.h>
+
+#include "chrome_frame/utils.h"
+
+namespace simulate_input {
+
+class ForegroundHelperWindow : public CWindowImpl<ForegroundHelperWindow> {
+ public:
+BEGIN_MSG_MAP(ForegroundHelperWindow)
+ MESSAGE_HANDLER(WM_HOTKEY, OnHotKey)
+END_MSG_MAP()
+
+ ForegroundHelperWindow() : window_(NULL) {}
+
+ HRESULT SetForeground(HWND window) {
+ DCHECK(::IsWindow(window));
+ window_ = window;
+ if (NULL == Create(NULL, NULL, NULL, WS_POPUP))
+ return AtlHresultFromLastError();
+
+ static const int kHotKeyId = 0x0000baba;
+ static const int kHotKeyWaitTimeout = 2000;
+
+ RegisterHotKey(m_hWnd, kHotKeyId, 0, VK_F22);
+
+ MSG msg = {0};
+ PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
+
+ SendMnemonic(VK_F22, false, false, false, false, false);
+ // There are scenarios where the WM_HOTKEY is not dispatched by the
+ // the corresponding foreground thread. To prevent us from indefinitely
+ // waiting for the hotkey, we set a timer and exit the loop.
+ SetTimer(kHotKeyId, kHotKeyWaitTimeout, NULL);
+
+ while (GetMessage(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ if (msg.message == WM_HOTKEY) {
+ break;
+ }
+ if (msg.message == WM_TIMER) {
+ SetForegroundWindow(window);
+ break;
+ }
+ }
+
+ UnregisterHotKey(m_hWnd, kHotKeyId);
+ KillTimer(kHotKeyId);
+ DestroyWindow();
+ return S_OK;
+ }
+
+ LRESULT OnHotKey(UINT msg, WPARAM wp, LPARAM lp, BOOL& handled) { // NOLINT
+ SetForegroundWindow(window_);
+ return 1;
+ }
+ private:
+ HWND window_;
+};
+
+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,
+ &current_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);
+ LOG_IF(ERROR, !ret) << "ForceSetForegroundWindow: " << ret;
+
+ return ret;
+}
+
+void SendChar(char c, bool control, bool alt) {
+ SendMnemonic(toupper(c), !!isupper(c), control, alt, false, false);
+}
+
+void SendChar(wchar_t c, bool control, bool alt) {
+ SendMnemonic(towupper(c), !!iswupper(c), control, alt, false, true);
+}
+
+// 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, bool extended, bool unicode) {
+ INPUT keys[4] = {0}; // Keyboard events
+ int key_count = 0; // Number of generated events
+
+ if (shift_pressed) {
+ keys[key_count].type = INPUT_KEYBOARD;
+ keys[key_count].ki.wVk = VK_SHIFT;
+ key_count++;
+ }
+
+ if (control_pressed) {
+ keys[key_count].type = INPUT_KEYBOARD;
+ keys[key_count].ki.wVk = VK_CONTROL;
+ key_count++;
+ }
+
+ if (alt_pressed) {
+ keys[key_count].type = INPUT_KEYBOARD;
+ keys[key_count].ki.wVk = VK_MENU;
+ key_count++;
+ }
+
+ keys[key_count].type = INPUT_KEYBOARD;
+ keys[key_count].ki.wVk = mnemonic_char;
+ if (extended)
+ keys[key_count].ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
+ if (unicode)
+ keys[key_count].ki.dwFlags |= KEYEVENTF_UNICODE;
+ key_count++;
+
+ // Send key downs
+ for (int i = 0; i < key_count; i++) {
+ SendInput(1, &keys[ i ], sizeof(keys[0]));
+ keys[i].ki.dwFlags |= KEYEVENTF_KEYUP;
+ }
+
+ // Now send key ups in reverse order
+ for (int i = key_count; i; i--) {
+ SendInput(1, &keys[ i - 1 ], sizeof(keys[0]));
+ }
+
+ return true;
+}
+
+void SetKeyboardFocusToWindow(HWND window) {
+ SendMouseClick(window, 1, 1, LEFT);
+}
+
+void SendMouseClick(HWND window, int x, int y, MouseButton button) {
+ if (!IsWindow(window)) {
+ NOTREACHED() << "Invalid window handle.";
+ return;
+ }
+
+ HWND top_level_window = window;
+ if (!IsTopLevelWindow(top_level_window)) {
+ top_level_window = GetAncestor(window, GA_ROOT);
+ }
+
+ ForceSetForegroundWindow(top_level_window);
+
+ POINT cursor_position = {x, y};
+ ClientToScreen(window, &cursor_position);
+
+ // TODO(joshia): Fix this. GetSystemMetrics(SM_CXSCREEN) will
+ // retrieve screen size of the primarary monitor only. And monitors
+ // arrangement could be pretty arbitrary.
+ double screen_width = ::GetSystemMetrics(SM_CXSCREEN) - 1;
+ double screen_height = ::GetSystemMetrics(SM_CYSCREEN) - 1;
+ double location_x = cursor_position.x * (65535.0f / screen_width);
+ double location_y = cursor_position.y * (65535.0f / screen_height);
+
+ // Take advantage of button flag bitmask layout
+ unsigned int button_flag = MOUSEEVENTF_LEFTDOWN << (button + button);
+
+ INPUT input_info = {0};
+ input_info.type = INPUT_MOUSE;
+ input_info.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
+ input_info.mi.dx = static_cast<LONG>(location_x);
+ input_info.mi.dy = static_cast<LONG>(location_y);
+ ::SendInput(1, &input_info, sizeof(INPUT));
+
+ Sleep(10);
+
+ input_info.mi.dwFlags = button_flag | MOUSEEVENTF_ABSOLUTE;
+ ::SendInput(1, &input_info, sizeof(INPUT));
+
+ Sleep(10);
+
+ input_info.mi.dwFlags = (button_flag << 1) | MOUSEEVENTF_ABSOLUTE;
+ ::SendInput(1, &input_info, sizeof(INPUT));
+}
+
+bool SendExtendedKey(WORD key, bool shift, bool control, bool alt) {
+ return SendMnemonic(key, shift, control, alt, true, false);
+}
+
+} // namespace simulate_input
+