summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-25 02:36:38 +0000
committerestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-25 02:36:38 +0000
commit64b31ba0bc8d530b6a900f0fcf84e23da65bc883 (patch)
treef6649711ebe6e0096bf0a902fda28d4a153d7f29 /chrome
parenta8b1bc7935276f4c004ac4cffefa50f45a47c9ed (diff)
downloadchromium_src-64b31ba0bc8d530b6a900f0fcf84e23da65bc883.zip
chromium_src-64b31ba0bc8d530b6a900f0fcf84e23da65bc883.tar.gz
chromium_src-64b31ba0bc8d530b6a900f0fcf84e23da65bc883.tar.bz2
GTK: First cut at tab dragging automation.
Also make tab dragging slightly more robust. I tried really hard to avoid hackiness, but after many hours of wrestling with gtk and X, this is the best I could do. The main point of contention is that GTK (and our tab dragging code in particular) seems to be able to get X into a state where gdk_display_warp_pointer() doesn't send back any events (although it does move the X pointer). I tried to fix our code directly, but decided it was GTK that was broken. So I faked some mouse motion events to prod the tab dragging into working. This approach does not appear to be flaky, and is actually closer to the event stream that occurs when a user drags a tab than the obvious approach would be. (The tests themselves are somewhat flaky, but only due to WaitForURLDisplayedForTab() flakiness, which is a separate issue I'll look at later. The tests aren't on any buildbot for now so I'd like to leave them enabled.) BUG=22182 TEST=--gtest_filter=AutomatedUITest.Drag* Review URL: http://codereview.chromium.org/218017 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@27166 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/automation/automation_provider_gtk.cc118
-rw-r--r--chrome/browser/automation/ui_controls_linux.cc50
-rw-r--r--chrome/browser/gtk/tabs/tab_gtk.cc25
-rw-r--r--chrome/test/automated_ui_tests/automated_ui_test_base.cc6
-rw-r--r--chrome/test/automated_ui_tests/automated_ui_test_interactive_test.cc10
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);