// Copyright (c) 2011 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/browser/automation/automation_provider.h"
#include <gtk/gtk.h>
#include "base/utf_string_conversions.h"
#include "chrome/browser/automation/automation_browser_tracker.h"
#include "chrome/browser/automation/automation_window_tracker.h"
#include "chrome/browser/automation/ui_controls.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/gtk/browser_window_gtk.h"
#include "chrome/browser/ui/gtk/gtk_util.h"
#include "chrome/browser/ui/gtk/view_id_util.h"
#include "chrome/common/automation_messages.h"
#include "ui/gfx/point.h"
#include "ui/gfx/rect.h"
void AutomationProvider::PrintAsync(int tab_handle) {
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,
const std::vector<gfx::Point>& drag_path,
int flags,
bool press_escape_en_route,
IPC::Message* 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);
}
}