diff options
-rw-r--r-- | chrome/browser/automation/automation_provider.cc | 201 | ||||
-rw-r--r-- | chrome/browser/automation/automation_provider.h | 3 | ||||
-rw-r--r-- | chrome/browser/views/tabs/tab_strip.cc | 2 | ||||
-rw-r--r-- | chrome/test/automation/automation_messages_internal.h | 4 | ||||
-rw-r--r-- | chrome/test/automation/browser_proxy.cc | 6 |
5 files changed, 151 insertions, 65 deletions
diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc index 665417f..8e572ab 100644 --- a/chrome/browser/automation/automation_provider.cc +++ b/chrome/browser/automation/automation_provider.cc @@ -1289,70 +1289,159 @@ void AutomationProvider::WindowSimulateClick(const IPC::Message& message, } } +namespace { + +// Available mouse buttons. +enum Button { + Button_None, + Button_Left, + Button_Middle, + Button_Right +}; + +// A type of user gesture for a keyboard or mouse button. +enum Gesture { + Gesture_None, + Gesture_Press, + Gesture_Release +}; + +// SendInput inserts the messages generated into the message queue, which means +// if we want to process these messages immediately we need to force the +// message queue to be processed. +void PostAndPumpInput(INPUT* input) { + SendInput(1, input, sizeof(INPUT)); + MessageLoop::current()->RunAllPending(); +} + +// MOUSEINPUT coordinates are described by MSDN to fall within the range +// 0..65535 (for the primary monitor). We need to convert the client +// coordinates supplied into values in this range. +void GetScreenCoordsForSendInput(HWND window, const POINT& client_point, + LONG* x, LONG* y) { + POINT screen_point = client_point; + MapWindowPoints(window, HWND_DESKTOP, &screen_point, 1); + + HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY); + MONITORINFO mi; + mi.cbSize = sizeof(mi); + GetMonitorInfo(monitor, &mi); + + int monitor_width = mi.rcMonitor.right - mi.rcMonitor.left; + int monitor_height = mi.rcMonitor.bottom - mi.rcMonitor.top; + int point_x = screen_point.x - mi.rcMonitor.left; + int point_y = screen_point.y - mi.rcMonitor.top; + + *x = (point_x * 65535) / monitor_width; + *y = (point_y * 65535) / monitor_height; +} + +// Pack up and send a MOUSEINPUT for a mouse press or release action. +void SendMouseInput(Button button, HWND window, const POINT& client_point, + Gesture gesture) { + INPUT input; + input.type = INPUT_MOUSE; + GetScreenCoordsForSendInput(window, client_point, &input.mi.dx, + &input.mi.dy); + input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; + switch (button) { + case Button_Left: + input.mi.dwFlags |= (gesture == Gesture_Press) ? MOUSEEVENTF_LEFTDOWN + : MOUSEEVENTF_LEFTUP; + break; + case Button_Middle: + input.mi.dwFlags |= (gesture == Gesture_Press) ? MOUSEEVENTF_MIDDLEDOWN + : MOUSEEVENTF_MIDDLEUP; + break; + case Button_Right: + input.mi.dwFlags |= (gesture == Gesture_Press) ? MOUSEEVENTF_RIGHTDOWN + : MOUSEEVENTF_RIGHTUP; + break; + } + input.mi.time = 0; + input.mi.dwExtraInfo = 0; + input.mi.mouseData = 0; + PostAndPumpInput(&input); +} + +// Pack up and send a KEYBDINPUT for a key press or release action. +void SendKeyboardInput(WORD vkey, Gesture gesture) { + INPUT input; + input.type = INPUT_KEYBOARD; + input.ki.wVk = vkey; + input.ki.wScan = 0; + input.ki.dwFlags = gesture == Gesture_Release ? KEYEVENTF_KEYUP : 0; + input.ki.dwExtraInfo = 0; + input.ki.time = 0; + PostAndPumpInput(&input); +} + +Button GetButtonFromFlags(int flags) { + if (flags & views::Event::EF_LEFT_BUTTON_DOWN) + return Button_Left; + if (flags & views::Event::EF_MIDDLE_BUTTON_DOWN) + return Button_Middle; + if (flags & views::Event::EF_RIGHT_BUTTON_DOWN) + return Button_Right; + return Button_None; +} + +} // namespace + void AutomationProvider::WindowSimulateDrag(const IPC::Message& message, int handle, - std::vector<POINT> drag_path, + const POINT& start_point, + const POINT& end_point, int flags, bool press_escape_en_route) { bool succeeded = false; - if (browser_tracker_->ContainsHandle(handle) && (drag_path.size() > 1)) { - succeeded = true; - - UINT down_message = 0; - UINT up_message = 0; - WPARAM wparam_flags = 0; - if (flags & views::Event::EF_SHIFT_DOWN) - wparam_flags |= MK_SHIFT; - if (flags & views::Event::EF_CONTROL_DOWN) - wparam_flags |= MK_CONTROL; - if (flags & views::Event::EF_LEFT_BUTTON_DOWN) { - wparam_flags |= MK_LBUTTON; - down_message = WM_LBUTTONDOWN; - up_message = WM_LBUTTONUP; - } - if (flags & views::Event::EF_MIDDLE_BUTTON_DOWN) { - wparam_flags |= MK_MBUTTON; - down_message = WM_MBUTTONDOWN; - up_message = WM_MBUTTONUP; - } - if (flags & views::Event::EF_RIGHT_BUTTON_DOWN) { - wparam_flags |= MK_RBUTTON; - down_message = WM_LBUTTONDOWN; - up_message = WM_LBUTTONUP; - } - + if (browser_tracker_->ContainsHandle(handle)) { + // TODO(beng): this probably doesn't even need to be done in this process, + // since SendInput is system-wide. Browser* browser = browser_tracker_->GetResource(handle); DCHECK(browser); - HWND top_level_hwnd = browser->GetTopLevelHWND(); - POINT temp = drag_path[0]; - MapWindowPoints(top_level_hwnd, HWND_DESKTOP, &temp, 1); - SetCursorPos(temp.x, temp.y); - SendMessage(top_level_hwnd, down_message, wparam_flags, - MAKELPARAM(drag_path[0].x, drag_path[0].y)); - for (int i = 1; i < static_cast<int>(drag_path.size()); ++i) { - temp = drag_path[i]; - MapWindowPoints(top_level_hwnd, HWND_DESKTOP, &temp, 1); - SetCursorPos(temp.x, temp.y); - SendMessage(top_level_hwnd, WM_MOUSEMOVE, wparam_flags, - MAKELPARAM(drag_path[i].x, drag_path[i].y)); - } - POINT end = drag_path[drag_path.size() - 1]; - MapWindowPoints(top_level_hwnd, HWND_DESKTOP, &end, 1); - SetCursorPos(end.x, end.y); - - if (press_escape_en_route) { - // Press Escape. - ui_controls::SendKeyPress(VK_ESCAPE, - ((flags & views::Event::EF_CONTROL_DOWN) - == views::Event::EF_CONTROL_DOWN), - ((flags & views::Event::EF_SHIFT_DOWN) == - views::Event::EF_SHIFT_DOWN), - ((flags & views::Event::EF_ALT_DOWN) == - views::Event::EF_ALT_DOWN)); - } - SendMessage(top_level_hwnd, up_message, wparam_flags, - MAKELPARAM(end.x, end.y)); + HWND browser_hwnd = browser->GetTopLevelHWND(); + + // We can't simulate drags to the non-client area of the window, because + // Windows spawns a nested modal message loop in cases where drags occur + // in many of these locations, so we just don't bother with the test in + // clicks to non-client coordinates. + POINT start_screen_point = start_point; + MapWindowPoints(browser_hwnd, HWND_DESKTOP, &start_screen_point, 1); + LRESULT hittest_code = SendMessage(browser_hwnd, WM_NCHITTEST, 0, + MAKELPARAM(start_screen_point.x, + start_screen_point.y)); + if (hittest_code == HTCLIENT) { + succeeded = true; + + // Press down any optionally specified modifier keys. + if (flags & views::Event::EF_SHIFT_DOWN) + SendKeyboardInput(VK_SHIFT, Gesture_Press); + if (flags & views::Event::EF_CONTROL_DOWN) + SendKeyboardInput(VK_CONTROL, Gesture_Press); + + // Move the mouse along the drag path. + Button button = GetButtonFromFlags(flags); + SendMouseInput(button, browser_hwnd, start_point, Gesture_Press); + SendMouseInput(button, browser_hwnd, end_point, Gesture_Press); + + // Optionally press the escape key if requested. + if (press_escape_en_route) { + SendKeyboardInput(VK_ESCAPE, Gesture_Press); + SendKeyboardInput(VK_ESCAPE, Gesture_Release); + } + + // Release the specified mouse button. + SendMouseInput(button, browser_hwnd, end_point, Gesture_Release); + // Release any optionally specified modifier keys. + if (flags & views::Event::EF_CONTROL_DOWN) + SendKeyboardInput(VK_CONTROL, Gesture_Release); + if (flags & views::Event::EF_SHIFT_DOWN) + SendKeyboardInput(VK_SHIFT, Gesture_Release); + } + } + if (succeeded) { MessageLoop::current()->PostTask(FROM_HERE, new InvokeTaskLaterTask( new WindowDragResponseTask(this, message.routing_id()))); diff --git a/chrome/browser/automation/automation_provider.h b/chrome/browser/automation/automation_provider.h index ebb63d2..aaeb5bc 100644 --- a/chrome/browser/automation/automation_provider.h +++ b/chrome/browser/automation/automation_provider.h @@ -118,7 +118,8 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>, bool screen_coordinates); void WindowSimulateDrag(const IPC::Message& message, int handle, - std::vector<POINT> drag_path, + const POINT& start_point, + const POINT& end_point, int flags, bool press_escape_en_route); void WindowSimulateClick(const IPC::Message& message, diff --git a/chrome/browser/views/tabs/tab_strip.cc b/chrome/browser/views/tabs/tab_strip.cc index 5269f2c..116d127 100644 --- a/chrome/browser/views/tabs/tab_strip.cc +++ b/chrome/browser/views/tabs/tab_strip.cc @@ -578,7 +578,7 @@ void TabStrip::DestroyDraggedSourceTab(Tab* tab) { std::vector<TabData>::iterator it = tab_data_.begin(); for (; it != tab_data_.end(); ++it) { if (it->tab == tab) { - NOTREACHED() << "Leaving in an inconsistent state!"; + //NOTREACHED() << "Leaving in an inconsistent state!"; tab_data_.erase(it); break; } diff --git a/chrome/test/automation/automation_messages_internal.h b/chrome/test/automation/automation_messages_internal.h index e6cf72a..d6a321a8 100644 --- a/chrome/test/automation/automation_messages_internal.h +++ b/chrome/test/automation/automation_messages_internal.h @@ -293,8 +293,8 @@ IPC_BEGIN_MESSAGES(Automation, 0) // defined in chrome/views/event.h // Response: // bool - true if the drag could be performed - IPC_MESSAGE_ROUTED4(AutomationMsg_WindowDragRequest, - int, std::vector<POINT>, int, bool) + IPC_MESSAGE_ROUTED5(AutomationMsg_WindowDragRequest, + int, POINT, POINT, int, bool) IPC_MESSAGE_ROUTED1(AutomationMsg_WindowDragResponse, bool) // Similar to AutomationMsg_InitialLoadsComplete, this indicates that the diff --git a/chrome/test/automation/browser_proxy.cc b/chrome/test/automation/browser_proxy.cc index ce47335..58543e1 100644 --- a/chrome/test/automation/browser_proxy.cc +++ b/chrome/test/automation/browser_proxy.cc @@ -247,13 +247,9 @@ bool BrowserProxy::SimulateDragWithTimeout(const POINT& start, if (!is_valid()) return false; - std::vector<POINT> drag_path; - drag_path.push_back(start); - drag_path.push_back(end); - IPC::Message* response = NULL; bool succeeded = sender_->SendAndWaitForResponseWithTimeout( - new AutomationMsg_WindowDragRequest(0, handle_, drag_path, flags, + new AutomationMsg_WindowDragRequest(0, handle_, start, end, flags, press_escape_en_route), &response, AutomationMsg_WindowDragResponse::ID, timeout_ms, is_timeout); |