diff options
-rw-r--r-- | chrome/browser/automation/automation_provider_gtk.cc | 118 | ||||
-rw-r--r-- | chrome/browser/automation/ui_controls_linux.cc | 50 | ||||
-rw-r--r-- | chrome/browser/gtk/tabs/tab_gtk.cc | 25 | ||||
-rw-r--r-- | chrome/test/automated_ui_tests/automated_ui_test_base.cc | 6 | ||||
-rw-r--r-- | chrome/test/automated_ui_tests/automated_ui_test_interactive_test.cc | 10 |
5 files changed, 177 insertions, 32 deletions
diff --git a/chrome/browser/automation/automation_provider_gtk.cc b/chrome/browser/automation/automation_provider_gtk.cc index 7036bf3..23e85b9 100644 --- a/chrome/browser/automation/automation_provider_gtk.cc +++ b/chrome/browser/automation/automation_provider_gtk.cc @@ -4,8 +4,11 @@ #include "chrome/browser/automation/automation_provider.h" +#include <gtk/gtk.h> + #include "base/gfx/point.h" #include "base/gfx/rect.h" +#include "chrome/browser/automation/ui_controls.h" #include "chrome/browser/gtk/browser_window_gtk.h" #include "chrome/browser/gtk/view_id_util.h" #include "chrome/common/gtk_util.h" @@ -94,14 +97,123 @@ void AutomationProvider::GetBookmarkBarVisibility(int handle, bool* visible, NOTIMPLEMENTED(); } +// This task sends a WindowDragResponse message with the appropriate +// routing ID to the automation proxy. This is implemented as a task so that +// we know that the mouse events (and any tasks that they spawn on the message +// loop) have been processed by the time this is sent. +class WindowDragResponseTask : public Task { + public: + WindowDragResponseTask(AutomationProvider* provider, + IPC::Message* reply_message) + : provider_(provider), + reply_message_(reply_message) { + DCHECK(provider_); + DCHECK(reply_message_); + } + + virtual ~WindowDragResponseTask() { + } + + virtual void Run() { + AutomationMsg_WindowDrag::WriteReplyParams(reply_message_, true); + provider_->Send(reply_message_); + } + + private: + AutomationProvider* provider_; + IPC::Message* reply_message_; + + DISALLOW_COPY_AND_ASSIGN(WindowDragResponseTask); +}; + +// A task that just runs a SendMouseEvent and performs another task when done. +class MouseEventTask : public Task { + public: + MouseEventTask(Task* next_task, ui_controls::MouseButtonState state) + : next_task_(next_task), + state_(state) {} + + virtual ~MouseEventTask() { + } + + virtual void Run() { + ui_controls::SendMouseEventsNotifyWhenDone(ui_controls::LEFT, state_, + next_task_); + } + + private: + // The task to execute when we are done. + Task* next_task_; + + // Mouse press or mouse release. + ui_controls::MouseButtonState state_; + + DISALLOW_COPY_AND_ASSIGN(MouseEventTask); +}; + +// A task that just runs a SendMouseMove and performs another task when done. +class MouseMoveTask : public Task { + public: + MouseMoveTask(Task* next_task, int absolute_x, int absolute_y) + : next_task_(next_task), + x_(absolute_x), + y_(absolute_y) { + } + + virtual ~MouseMoveTask() { + } + + virtual void Run() { + ui_controls::SendMouseMoveNotifyWhenDone(x_, y_, next_task_); + } + + private: + // The task to execute when we are done. + Task* next_task_; + + // Coordinates of the press. + int x_; + int y_; + + DISALLOW_COPY_AND_ASSIGN(MouseMoveTask); +}; + void AutomationProvider::WindowSimulateDrag(int handle, std::vector<gfx::Point> drag_path, int flags, bool press_escape_en_route, IPC::Message* reply_message) { - NOTIMPLEMENTED(); - AutomationMsg_WindowDrag::WriteReplyParams(reply_message, false); - Send(reply_message); + // TODO(estade): don't ignore |flags| or |escape_en_route|. + gfx::NativeWindow window = + browser_tracker_->GetResource(handle)->window()->GetNativeHandle(); + if (window && (drag_path.size() > 1)) { + int x, y; + gdk_window_get_position(GTK_WIDGET(window)->window, &x, &y); + + // Create a nested stack of tasks to run. + Task* next_task = new WindowDragResponseTask(this, reply_message); + next_task = new MouseEventTask(next_task, ui_controls::UP); + next_task = new MouseEventTask(next_task, ui_controls::UP); + for (size_t i = drag_path.size() - 1; i > 0; --i) { + // Smooth out the mouse movements by adding intermediate points. This + // better simulates a real user drag. + int dest_x = drag_path[i].x() + x; + int dest_y = drag_path[i].y() + y; + int half_step_x = (dest_x + drag_path[i - 1].x() + x) / 2; + int half_step_y = (dest_y + drag_path[i - 1].y() + y) / 2; + + next_task = new MouseMoveTask(next_task, dest_x, dest_y); + next_task = new MouseMoveTask(next_task, half_step_x, half_step_y); + } + next_task = new MouseEventTask(next_task, ui_controls::DOWN); + + ui_controls::SendMouseMoveNotifyWhenDone(x + drag_path[0].x(), + y + drag_path[0].y(), + next_task); + } else { + AutomationMsg_WindowDrag::WriteReplyParams(reply_message, false); + Send(reply_message); + } } void AutomationProvider::TerminateSession(int handle, bool* success) { diff --git a/chrome/browser/automation/ui_controls_linux.cc b/chrome/browser/automation/ui_controls_linux.cc index aa18ce2..6b2deee 100644 --- a/chrome/browser/automation/ui_controls_linux.cc +++ b/chrome/browser/automation/ui_controls_linux.cc @@ -58,9 +58,9 @@ class EventWaiter : public MessageLoopForUI::Observer { } private: - // We pass ownership of task_ to MessageLoop when the corrent event is + // We pass ownership of task_ to MessageLoop when the current event is // received. - Task *task_; + Task* task_; GdkEventType type_; // The number of events of this type to wait for. int count_; @@ -87,7 +87,6 @@ class ClickTask : public Task { Task* followup_; }; - bool SendKeyEvent(GdkWindow* window, bool press, guint key, guint state) { GdkEvent* event = gdk_event_new(press ? GDK_KEY_PRESS : GDK_KEY_RELEASE); @@ -116,6 +115,35 @@ bool SendKeyEvent(GdkWindow* window, bool press, guint key, guint state) { return true; } +void FakeAMouseMotionEvent(gint x, gint y) { + GdkEvent* event = gdk_event_new(GDK_MOTION_NOTIFY); + + event->motion.send_event = false; + event->motion.time = EventTimeNow(); + + GtkWidget* grab_widget = gtk_grab_get_current(); + if (grab_widget) { + // If there is a grab, we need to target all events at it regardless of + // what widget the mouse is over. + event->motion.window = grab_widget->window; + } else { + event->motion.window = gdk_window_at_pointer(&x, &y); + } + g_object_ref(event->motion.window); + event->motion.x = x; + event->motion.y = y; + gint origin_x, origin_y; + gdk_window_get_origin(event->motion.window, &origin_x, &origin_y); + event->motion.x_root = x + origin_x; + event->motion.y_root = y + origin_y; + + event->motion.device = gdk_device_get_core_pointer(); + event->type = GDK_MOTION_NOTIFY; + + gdk_event_put(event); + gdk_event_free(event); +} + } // namespace namespace ui_controls { @@ -205,13 +233,18 @@ bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, wchar_t key, bool SendMouseMove(long x, long y) { gdk_display_warp_pointer(gdk_display_get_default(), gdk_screen_get_default(), x, y); + // Sometimes gdk_display_warp_pointer fails to send back any indication of + // the move, even though it succesfully moves the server cursor. We fake it in + // order to get drags to work. + FakeAMouseMotionEvent(x, y); + return true; } bool SendMouseMoveNotifyWhenDone(long x, long y, Task* task) { bool rv = SendMouseMove(x, y); // We can't rely on any particular event signalling the completion of the - // mouse move. Posting the task to the message loop should gaurantee + // mouse move. Posting the task to the message loop hopefully guarantees // the pointer has moved before task is run (although it may not run it as // soon as it could). MessageLoop::current()->PostTask(FROM_HERE, task); @@ -234,17 +267,18 @@ bool SendMouseEvents(MouseButton type, int state) { } else { event->button.window = gdk_window_at_pointer(&x, &y); } + g_object_ref(event->button.window); - event->motion.x = x; - event->motion.y = y; + event->button.x = x; + event->button.y = y; gint origin_x, origin_y; gdk_window_get_origin(event->button.window, &origin_x, &origin_y); event->button.x_root = x + origin_x; event->button.y_root = y + origin_y; event->button.axes = NULL; - // TODO(estade): as above, we may want to pack this with the actual state. - event->button.state = 0; + gdk_window_get_pointer(event->button.window, NULL, NULL, + reinterpret_cast<GdkModifierType*>(&event->button.state)); event->button.button = type == LEFT ? 1 : (type == MIDDLE ? 2 : 3); event->button.device = gdk_device_get_core_pointer(); diff --git a/chrome/browser/gtk/tabs/tab_gtk.cc b/chrome/browser/gtk/tabs/tab_gtk.cc index 3850da9..904edc3 100644 --- a/chrome/browser/gtk/tabs/tab_gtk.cc +++ b/chrome/browser/gtk/tabs/tab_gtk.cc @@ -218,24 +218,27 @@ void TabGtk::WillProcessEvent(GdkEvent* event) { } void TabGtk::DidProcessEvent(GdkEvent* event) { - if (event->type != GDK_MOTION_NOTIFY) + if (!(event->type == GDK_MOTION_NOTIFY || event->type == GDK_LEAVE_NOTIFY || + event->type == GDK_ENTER_NOTIFY)) { return; + } if (drag_widget_) { delegate_->ContinueDrag(NULL); return; } - GdkEventMotion* motion = reinterpret_cast<GdkEventMotion*>(event); - GdkEventButton* button = reinterpret_cast<GdkEventButton*>(last_mouse_down_); - bool dragging = gtk_drag_check_threshold(widget(), - static_cast<gint>(button->x), - static_cast<gint>(button->y), - static_cast<gint>(motion->x), - static_cast<gint>(motion->y)); - if (dragging) { - StartDragging(gfx::Point(static_cast<int>(button->x), - static_cast<int>(button->y))); + gint old_x = static_cast<gint>(last_mouse_down_->button.x_root); + gint old_y = static_cast<gint>(last_mouse_down_->button.y_root); + gdouble new_x; + gdouble new_y; + gdk_event_get_root_coords(event, &new_x, &new_y); + + if (gtk_drag_check_threshold(widget(), old_x, old_y, + static_cast<gint>(new_x), static_cast<gint>(new_y))) { + StartDragging(gfx::Point( + static_cast<int>(last_mouse_down_->button.x), + static_cast<int>(last_mouse_down_->button.y))); } } diff --git a/chrome/test/automated_ui_tests/automated_ui_test_base.cc b/chrome/test/automated_ui_tests/automated_ui_test_base.cc index 81ed10e..9880fd3 100644 --- a/chrome/test/automated_ui_tests/automated_ui_test_base.cc +++ b/chrome/test/automated_ui_tests/automated_ui_test_base.cc @@ -17,12 +17,14 @@ AutomatedUITestBase::AutomatedUITestBase() {} AutomatedUITestBase::~AutomatedUITestBase() {} -void AutomatedUITestBase::LogErrorMessage(const std::string& error) {} +void AutomatedUITestBase::LogErrorMessage(const std::string& error) { +} void AutomatedUITestBase::LogWarningMessage(const std::string& warning) { } -void AutomatedUITestBase::LogInfoMessage(const std::string& info) {} +void AutomatedUITestBase::LogInfoMessage(const std::string& info) { +} void AutomatedUITestBase::SetUp() { UITest::SetUp(); diff --git a/chrome/test/automated_ui_tests/automated_ui_test_interactive_test.cc b/chrome/test/automated_ui_tests/automated_ui_test_interactive_test.cc index f7ac5fc..e1d2e64 100644 --- a/chrome/test/automated_ui_tests/automated_ui_test_interactive_test.cc +++ b/chrome/test/automated_ui_tests/automated_ui_test_interactive_test.cc @@ -7,12 +7,6 @@ #include "chrome/test/automation/tab_proxy.h" #include "chrome/test/ui/ui_test.h" -#if defined(OS_WINDOWS) -#define MAYBE(x) x -#else -#define MAYBE(x) DISABLED_##x -#endif - namespace { bool WaitForURLDisplayedForTab(BrowserProxy* browser, int tab_index, @@ -32,7 +26,7 @@ bool WaitForURLDisplayedForTab(BrowserProxy* browser, int tab_index, } // namespace -TEST_F(AutomatedUITestBase, MAYBE(DragOut)) { +TEST_F(AutomatedUITestBase, DragOut) { int tab_count; active_browser()->GetTabCount(&tab_count); ASSERT_EQ(1, tab_count); @@ -54,7 +48,7 @@ TEST_F(AutomatedUITestBase, MAYBE(DragOut)) { ASSERT_EQ(2, window_count); } -TEST_F(AutomatedUITestBase, MAYBE(DragLeftRight)) { +TEST_F(AutomatedUITestBase, DragLeftRight) { int tab_count; active_browser()->GetTabCount(&tab_count); ASSERT_EQ(1, tab_count); |