1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/automation/automation_provider.h"
6
7#include <gtk/gtk.h>
8
9#include "base/utf_string_conversions.h"
10#include "chrome/browser/automation/automation_browser_tracker.h"
11#include "chrome/browser/automation/automation_window_tracker.h"
12#include "chrome/browser/automation/ui_controls.h"
13#include "chrome/browser/ui/browser.h"
14#include "chrome/browser/ui/gtk/browser_window_gtk.h"
15#include "chrome/browser/ui/gtk/gtk_util.h"
16#include "chrome/browser/ui/gtk/view_id_util.h"
17#include "chrome/common/automation_messages.h"
18#include "ui/gfx/point.h"
19#include "ui/gfx/rect.h"
20
21void AutomationProvider::PrintAsync(int tab_handle) {
22  NOTIMPLEMENTED();
23}
24
25// This task sends a WindowDragResponse message with the appropriate
26// routing ID to the automation proxy.  This is implemented as a task so that
27// we know that the mouse events (and any tasks that they spawn on the message
28// loop) have been processed by the time this is sent.
29class WindowDragResponseTask : public Task {
30 public:
31  WindowDragResponseTask(AutomationProvider* provider,
32                         IPC::Message* reply_message)
33      : provider_(provider),
34        reply_message_(reply_message) {
35    DCHECK(provider_);
36    DCHECK(reply_message_);
37  }
38
39  virtual ~WindowDragResponseTask() {
40  }
41
42  virtual void Run() {
43    AutomationMsg_WindowDrag::WriteReplyParams(reply_message_, true);
44    provider_->Send(reply_message_);
45  }
46
47 private:
48  AutomationProvider* provider_;
49  IPC::Message* reply_message_;
50
51  DISALLOW_COPY_AND_ASSIGN(WindowDragResponseTask);
52};
53
54// A task that just runs a SendMouseEvent and performs another task when done.
55class MouseEventTask : public Task {
56 public:
57  MouseEventTask(Task* next_task, ui_controls::MouseButtonState state)
58      : next_task_(next_task),
59        state_(state) {}
60
61  virtual ~MouseEventTask() {
62  }
63
64  virtual void Run() {
65    ui_controls::SendMouseEventsNotifyWhenDone(ui_controls::LEFT, state_,
66                                               next_task_);
67  }
68
69 private:
70  // The task to execute when we are done.
71  Task* next_task_;
72
73  // Mouse press or mouse release.
74  ui_controls::MouseButtonState state_;
75
76  DISALLOW_COPY_AND_ASSIGN(MouseEventTask);
77};
78
79// A task that just runs a SendMouseMove and performs another task when done.
80class MouseMoveTask : public Task {
81 public:
82  MouseMoveTask(Task* next_task, int absolute_x, int absolute_y)
83      : next_task_(next_task),
84        x_(absolute_x),
85        y_(absolute_y) {
86  }
87
88  virtual ~MouseMoveTask() {
89  }
90
91  virtual void Run() {
92    ui_controls::SendMouseMoveNotifyWhenDone(x_, y_, next_task_);
93  }
94
95 private:
96  // The task to execute when we are done.
97  Task* next_task_;
98
99  // Coordinates of the press.
100  int x_;
101  int y_;
102
103  DISALLOW_COPY_AND_ASSIGN(MouseMoveTask);
104};
105
106void AutomationProvider::WindowSimulateDrag(
107    int handle,
108    const std::vector<gfx::Point>& drag_path,
109    int flags,
110    bool press_escape_en_route,
111    IPC::Message* reply_message) {
112  // TODO(estade): don't ignore |flags| or |escape_en_route|.
113  gfx::NativeWindow window =
114      browser_tracker_->GetResource(handle)->window()->GetNativeHandle();
115  if (window && (drag_path.size() > 1)) {
116    int x, y;
117    gdk_window_get_position(GTK_WIDGET(window)->window, &x, &y);
118
119    // Create a nested stack of tasks to run.
120    Task* next_task = new WindowDragResponseTask(this, reply_message);
121    next_task = new MouseEventTask(next_task, ui_controls::UP);
122    next_task = new MouseEventTask(next_task, ui_controls::UP);
123    for (size_t i = drag_path.size() - 1; i > 0; --i) {
124      // Smooth out the mouse movements by adding intermediate points. This
125      // better simulates a real user drag.
126      int dest_x = drag_path[i].x() + x;
127      int dest_y = drag_path[i].y() + y;
128      int half_step_x = (dest_x + drag_path[i - 1].x() + x) / 2;
129      int half_step_y = (dest_y + drag_path[i - 1].y() + y) / 2;
130
131      next_task = new MouseMoveTask(next_task, dest_x, dest_y);
132      next_task = new MouseMoveTask(next_task, half_step_x, half_step_y);
133    }
134    next_task = new MouseEventTask(next_task, ui_controls::DOWN);
135
136    ui_controls::SendMouseMoveNotifyWhenDone(x + drag_path[0].x(),
137                                             y + drag_path[0].y(),
138                                             next_task);
139  } else {
140    AutomationMsg_WindowDrag::WriteReplyParams(reply_message, false);
141    Send(reply_message);
142  }
143}
144